Full Code of minvws/nl-kat-coordination for AI

main 760407b8f871 cached
2492 files
28.6 MB
3.9M tokens
5266 symbols
1 requests
Copy disabled (too large) Download .txt
Showing preview only (15,829K chars total). Download the full file to get everything.
Repository: minvws/nl-kat-coordination
Branch: main
Commit: 760407b8f871
Files: 2492
Total size: 28.6 MB

Directory structure:
gitextract_do4ph06x/

├── .dockerignore
├── .env-defaults
├── .env-dist
├── .env-prod
├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── .gitignore
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   ├── feature_request.md
│   │   └── user_story.md
│   ├── dependabot.yml
│   ├── pull_request_template.md
│   ├── scripts/
│   │   ├── commit_sign_push.sh
│   │   └── coverage_file_fixer.py
│   └── workflows/
│       ├── boefjes-tests.yml
│       ├── boefjes_container_image.yml
│       ├── boefjes_tests.yml
│       ├── build-debian-docker-image.yml
│       ├── build-rdo-package.yml
│       ├── build_docs_on_pr.yml
│       ├── bytes-tests.yml
│       ├── bytes_container_image.yml
│       ├── bytes_tests.yml
│       ├── check_requirements.yml
│       ├── codeql.yml
│       ├── containerized_boefjes.yml
│       ├── debian_package.yml
│       ├── deploy_docs.yml
│       ├── mula-tests.yml
│       ├── mula_container_image.yml
│       ├── mula_tests.yml
│       ├── octopoes-tests.yml
│       ├── octopoes_container_image.yml
│       ├── octopoes_rtest.yml
│       ├── octopoes_tests.yml
│       ├── pre_commit_checks.yml
│       ├── rocky-tests.yml
│       ├── rocky_container_image.yml
│       ├── rocky_makelang.yml
│       ├── rocky_tests.yml
│       ├── sonar-cloud.yml
│       └── test_debian_packages_on_ubuntu.yml
├── .gitignore
├── .gitpod.yml
├── .pre-commit-config.yaml
├── .stylelintrc.json
├── CONTRIBUTING.rst
├── LICENSE
├── Makefile
├── README.rst
├── boefjes/
│   ├── .ci/
│   │   └── docker-compose.yml
│   ├── .dockerignore
│   ├── .editorconfig
│   ├── .gitignore
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── boefjes/
│   │   ├── __init__.py
│   │   ├── __main__.py
│   │   ├── alembic.ini
│   │   ├── api.py
│   │   ├── clients/
│   │   │   ├── __init__.py
│   │   │   ├── bytes_client.py
│   │   │   └── scheduler_client.py
│   │   ├── config.py
│   │   ├── dependencies/
│   │   │   ├── __init__.py
│   │   │   ├── encryption.py
│   │   │   └── plugins.py
│   │   ├── job_handler.py
│   │   ├── katalogus/
│   │   │   ├── __init__.py
│   │   │   ├── configs.py
│   │   │   ├── organisations.py
│   │   │   ├── plugins.py
│   │   │   ├── root.py
│   │   │   ├── settings.py
│   │   │   └── version.py
│   │   ├── local/
│   │   │   ├── __init__.py
│   │   │   └── runner.py
│   │   ├── logging.json
│   │   ├── logging.py
│   │   ├── migrations/
│   │   │   ├── __init__.py
│   │   │   ├── env.py
│   │   │   ├── script.py.mako
│   │   │   └── versions/
│   │   │       ├── 0001_add_katalogus_models.py
│   │   │       ├── 0002_change_lengths_of_several_char_fields.py
│   │   │       ├── 0003_longer_plugin_ids.py
│   │   │       ├── 197672984df0_make_organisation_code_field_larger.py
│   │   │       ├── 5be152459a7b_introduce_schema_field_to_boefje_model.py
│   │   │       ├── 6f99834a4a5a_introduce_boefje_and_normalizer_models.py
│   │   │       ├── 7c88b9cd96aa_remove_the_repository_model.py
│   │   │       ├── 870fc302b852_remove_environment_keys_field.py
│   │   │       ├── 9f48560b0000_add_schedule_interval_fields.py
│   │   │       ├── __init__.py
│   │   │       ├── a2c8d54b0124_unique_plugin_names.py
│   │   │       ├── cd34fdfafdaf_json_settings_for_settings_table.py
│   │   │       ├── f9de6eb7824b_introduce_boefjeconfig_model.py
│   │   │       ├── fc0295b38184_add_run_on_field_to_boefje.py
│   │   │       └── fdeaea4481b8_add_deduplication_flag.py
│   │   ├── normalizer_interfaces.py
│   │   ├── normalizer_models.py
│   │   ├── plugins/
│   │   │   ├── __init__.py
│   │   │   ├── helpers.py
│   │   │   ├── kat_adr_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── adr_finding_types.json
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_adr_validator/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_answer_parser/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_binaryedge/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── containers/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── databases/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── description.md
│   │   │   │   ├── http_web/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── main.py
│   │   │   │   ├── message_queues/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── protocols/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── remote_desktop/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   ├── schema.json
│   │   │   │   ├── service_identification/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   └── services/
│   │   │   │       ├── __init__.py
│   │   │   │       ├── normalize.py
│   │   │   │       └── normalizer.json
│   │   │   ├── kat_burpsuite/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_calvin/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_censys/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_crt_sh/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_cve_2023_34039/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── keys/
│   │   │   │   │   ├── vrni-6.0.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.0.0_platform
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.0.0_proxy
│   │   │   │   │   ├── vrni-6.1.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.1.0_platform
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.1.0_proxy
│   │   │   │   │   ├── vrni-6.10.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.10.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.10.0_platform
│   │   │   │   │   ├── vrni-6.2.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.2.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.2.0_platform
│   │   │   │   │   ├── vrni-6.3.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.3.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.3.0_platform
│   │   │   │   │   ├── vrni-6.4.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.4.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.4.0_platform
│   │   │   │   │   ├── vrni-6.5.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.5.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.5.0_platform
│   │   │   │   │   ├── vrni-6.6.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.6.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.6.0_platform
│   │   │   │   │   ├── vrni-6.7.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.7.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.7.0_platform
│   │   │   │   │   ├── vrni-6.8.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.8.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.8.0_platform
│   │   │   │   │   └── vrni-6.9.0/
│   │   │   │   │       ├── id_rsa_vnera_keypair_6.9.0_collector
│   │   │   │   │       └── id_rsa_vnera_keypair_6.9.0_platform
│   │   │   │   └── main.py
│   │   │   ├── kat_cve_2023_35078/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_cve_2024_6387/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_cve_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_cwe_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── cwec_v4.16.xml
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_dicom/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_dns/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_dns_version/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_dns_zone/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_dnssec/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_export_http/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   └── schema.json
│   │   │   ├── kat_external_db/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_fierce/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── fierce.py
│   │   │   │   ├── lists/
│   │   │   │   │   ├── 20000.txt
│   │   │   │   │   ├── 5000.txt
│   │   │   │   │   └── default.txt
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_finding_normalizer/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_green_hosting/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_kat_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── kat_finding_types.json
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_leakix/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_manual/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── csv/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   └── single_ooi/
│   │   │   │       ├── __init__.py
│   │   │   │       ├── normalize.py
│   │   │   │       └── normalizer.json
│   │   │   ├── kat_masscan/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_maxmind_geoip/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_nikto/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.js
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── oci_adapter.js
│   │   │   │   ├── package.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_nmap_ip_range/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   └── schema.json
│   │   │   ├── kat_nmap_ports/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   └── schema.json
│   │   │   ├── kat_nmap_tcp/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_nmap_udp/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   └── schema.json
│   │   │   ├── kat_nuclei_cve/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_nuclei_exposed_panels/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_nuclei_take_over/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_pdio_subfinder/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_rdns/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_report_data/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_retirejs_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_rpki/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_security_txt_downloader/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_service_banner/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── kat_shodan/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_shodan_internetdb/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_snyk/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── check_version.py
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_snyk_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_ssl_certificates/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_ssl_scan/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_testssl_sh_ciphers/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── boefjes-requirements.txt
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_wappalyzer/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── data/
│   │   │   │   │   ├── categories.json
│   │   │   │   │   ├── groups.json
│   │   │   │   │   ├── technologies/
│   │   │   │   │   │   ├── _.json
│   │   │   │   │   │   ├── a.json
│   │   │   │   │   │   ├── b.json
│   │   │   │   │   │   ├── c.json
│   │   │   │   │   │   ├── d.json
│   │   │   │   │   │   ├── e.json
│   │   │   │   │   │   ├── f.json
│   │   │   │   │   │   ├── g.json
│   │   │   │   │   │   ├── h.json
│   │   │   │   │   │   ├── i.json
│   │   │   │   │   │   ├── j.json
│   │   │   │   │   │   ├── k.json
│   │   │   │   │   │   ├── l.json
│   │   │   │   │   │   ├── m.json
│   │   │   │   │   │   ├── n.json
│   │   │   │   │   │   ├── o.json
│   │   │   │   │   │   ├── p.json
│   │   │   │   │   │   ├── q.json
│   │   │   │   │   │   ├── r.json
│   │   │   │   │   │   ├── s.json
│   │   │   │   │   │   ├── t.json
│   │   │   │   │   │   ├── u.json
│   │   │   │   │   │   ├── v.json
│   │   │   │   │   │   ├── w.json
│   │   │   │   │   │   ├── x.json
│   │   │   │   │   │   ├── y.json
│   │   │   │   │   │   └── z.json
│   │   │   │   │   ├── update.sh
│   │   │   │   │   └── urls.txt
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── utils.py
│   │   │   ├── kat_webpage_analysis/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── check_images/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── description.md
│   │   │   │   ├── find_images_in_html/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── har/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── httpx.py
│   │   │   │   │   └── requests.py
│   │   │   │   ├── headers/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── main.py
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_webpage_capture/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   └── kat_wpscan/
│   │   │       ├── __init__.py
│   │   │       ├── boefje.Dockerfile
│   │   │       ├── boefje.Dockerfile.dockerignore
│   │   │       ├── boefje.json
│   │   │       ├── description.md
│   │   │       ├── main.py
│   │   │       ├── normalize.py
│   │   │       ├── normalizer.json
│   │   │       └── schema.json
│   │   ├── seed.py
│   │   ├── sql/
│   │   │   ├── __init__.py
│   │   │   ├── config_storage.py
│   │   │   ├── db.py
│   │   │   ├── db_models.py
│   │   │   ├── organisation_storage.py
│   │   │   ├── plugin_storage.py
│   │   │   └── session.py
│   │   ├── storage/
│   │   │   ├── __init__.py
│   │   │   ├── interfaces.py
│   │   │   └── memory.py
│   │   ├── version.py
│   │   └── worker/
│   │       ├── __init__.py
│   │       ├── __main__.py
│   │       ├── boefje_handler.py
│   │       ├── client.py
│   │       ├── interfaces.py
│   │       ├── job_models.py
│   │       ├── manager.py
│   │       ├── models.py
│   │       ├── oci_adapter.py
│   │       └── repository.py
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── install
│   │   ├── kat-boefjes.kat-boefjes.service
│   │   ├── kat-boefjes.kat-katalogus.service
│   │   ├── kat-boefjes.kat-normalizers.service
│   │   ├── kat-boefjes.sysusers
│   │   ├── postinst
│   │   ├── rules
│   │   └── triggers
│   ├── entrypoint.sh
│   ├── export_migrations/
│   │   ├── 0001_add_katalogus_models.sql
│   │   ├── 0002_change_lengths_of_several_char_fields.sql
│   │   ├── 0003_longer_plugin_ids.sql
│   │   ├── 0004_197672984df0_make_organisation_code_field_larger.sql
│   │   ├── 0005_cd34fdfafdaf_json_settings_for_settings_table.sql
│   │   ├── 0006_7c88b9cd96aa_remove_the_repository_model.sql
│   │   ├── 0007_6f99834a4a5a_introduce_boefje_and_normalizer_models.sql
│   │   ├── 0008_f9de6eb7824b_introduce_boefjeconfig_model.sql
│   │   ├── 0009_5be152459a7b_introduce_schema_field_to_boefje_model.sql
│   │   ├── 0010_870fc302b852_remove_environment_keys_field.sql
│   │   ├── 0011_a2c8d54b0124_unique_plugin_names.sql
│   │   ├── 0012_9f48560b0000_add_schedule_interval_fields.sql
│   │   ├── 0013_fc0295b38184_add_run_on_field_to_boefje.sql
│   │   └── 0014_fdeaea4481b8_add_deduplication_flag.sql
│   ├── images/
│   │   ├── base.Dockerfile
│   │   ├── base.Dockerfile.dockerignore
│   │   ├── generic.Dockerfile
│   │   ├── generic.Dockerfile.dockerignore
│   │   └── requirements.txt
│   ├── packaging/
│   │   ├── deb/
│   │   │   ├── Makefile
│   │   │   └── data/
│   │   │       ├── etc/
│   │   │       │   └── kat/
│   │   │       │       ├── boefjes.conf
│   │   │       │       ├── boefjes.logging.json
│   │   │       │       └── katalogus.gunicorn.conf.py
│   │   │       └── usr/
│   │   │           └── bin/
│   │   │               └── update-katalogus-db
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── setup.py
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── examples/
│   │   │   ├── adr-validator-normalize.json
│   │   │   ├── answer-normalize.json
│   │   │   ├── body-normalize.json
│   │   │   ├── body-page-analysis-normalize.json
│   │   │   ├── bodyimage-normalize.json
│   │   │   ├── calvin-normalizer.json
│   │   │   ├── cat_image
│   │   │   ├── cve_2023_35078_not_vulnerable.html
│   │   │   ├── cve_2023_35078_vulnerable.html
│   │   │   ├── dns-normalize.json
│   │   │   ├── download_body
│   │   │   ├── download_headers.json
│   │   │   ├── download_image_headers.json
│   │   │   ├── download_page_analysis.raw
│   │   │   ├── external_db.json
│   │   │   ├── inputs/
│   │   │   │   ├── cve-result-with-cvss.json
│   │   │   │   ├── cve-result-with-cvss2.json
│   │   │   │   ├── cve-result-without-cvss.json
│   │   │   │   ├── dns-result-example.com-cnames.json
│   │   │   │   ├── dns-result-example.nl.json
│   │   │   │   ├── dns-result-mx-example.nl.json
│   │   │   │   ├── dns-result-www.example.nl.json
│   │   │   │   ├── dns-zone-result-sub.example.nl.txt
│   │   │   │   ├── dnssec-self-signed.txt
│   │   │   │   ├── dnssec-status-line-not-last-line.txt
│   │   │   │   ├── dnssec-unsigned.txt
│   │   │   │   ├── dnssec-valid.txt
│   │   │   │   ├── fierce-result-example.com.json
│   │   │   │   ├── headers-check-input.json
│   │   │   │   ├── security_txt_result_different_website.json
│   │   │   │   ├── security_txt_result_same_website.json
│   │   │   │   ├── snyk-result-findings.json
│   │   │   │   ├── snyk-result-no-findings.json
│   │   │   │   ├── testssl-sh-ciphered.json
│   │   │   │   └── testssl-sh-cipherless.json
│   │   │   ├── log4shell-job.json
│   │   │   ├── manual-csv.json
│   │   │   ├── manual-ooi.json
│   │   │   ├── raw/
│   │   │   │   ├── leakix-example.com-output.txt
│   │   │   │   ├── leakix-example.com.json
│   │   │   │   ├── leakix-hostname-strict.json
│   │   │   │   ├── nikto-apache.dvwa.cloud.json
│   │   │   │   ├── nikto-example.com.json
│   │   │   │   ├── nikto-non-existing.com.json
│   │   │   │   └── nmap_mispoes.xml
│   │   │   ├── rdns-example1.txt
│   │   │   ├── rdns-example2.txt
│   │   │   ├── rdns-nxdomain.txt
│   │   │   ├── report-data-normalize.json
│   │   │   ├── report-data.json
│   │   │   ├── scheduler/
│   │   │   │   ├── pop_response_boefje.json
│   │   │   │   ├── pop_response_boefje_2.json
│   │   │   │   ├── pop_response_boefje_no_ooi.json
│   │   │   │   ├── pop_response_duplicated_boefje.json
│   │   │   │   ├── pop_response_duplicated_boefje_error.json
│   │   │   │   ├── pop_response_duplicated_docker_boefje.json
│   │   │   │   ├── pop_response_normalizer.json
│   │   │   │   ├── should_crash.json
│   │   │   │   └── should_crash_2.json
│   │   │   ├── snyk-job.json
│   │   │   ├── snyk-normalizer.json
│   │   │   ├── snyk-vuln.html
│   │   │   ├── snyk-vuln2.html
│   │   │   ├── snyk-vuln3.html
│   │   │   ├── ssl-certificates.txt
│   │   │   ├── user-changed.json
│   │   │   ├── user-login-admin-failure.json
│   │   │   ├── user-login-failure.json
│   │   │   └── webpage-analysis.json
│   │   ├── integration/
│   │   │   ├── __init__.py
│   │   │   ├── test_api.py
│   │   │   ├── test_bench.py
│   │   │   ├── test_get_environment.py
│   │   │   ├── test_json_settings_encryption_migration.py
│   │   │   ├── test_migration_add_schema_field.py
│   │   │   ├── test_remove_repository_migration.py
│   │   │   ├── test_settings_to_boefje_config_migration.py
│   │   │   └── test_sql_repositories.py
│   │   ├── katalogus/
│   │   │   ├── __init__.py
│   │   │   ├── test_organisation_api.py
│   │   │   ├── test_plugin_service.py
│   │   │   ├── test_plugins_api.py
│   │   │   └── test_settings.py
│   │   ├── loading.py
│   │   ├── modules/
│   │   │   ├── __init__.py
│   │   │   ├── dummy_bad_normalizer_dict_structure/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── dummy_bad_normalizer_return_type/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── dummy_boefje/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_boefje_environment/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_boefje_environment_with_pycache/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── main.py
│   │   │   │   └── some_subdir/
│   │   │   │       └── __init__.py
│   │   │   ├── dummy_boefje_invalid_signature/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_boefje_missing_run/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_boefje_runtime_exception/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_normalizer/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── dummy_normalizer_import_error/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── dummy_oci_boefje_no_main/
│   │   │   │   ├── __init__.py
│   │   │   │   └── boefje.json
│   │   │   └── kat_test/
│   │   │       ├── __init__.py
│   │   │       ├── boefje.json
│   │   │       ├── kat_test_2/
│   │   │       │   ├── __init__.py
│   │   │       │   ├── boefje.json
│   │   │       │   ├── kat_test_3/
│   │   │       │   │   ├── __init__.py
│   │   │       │   │   ├── normalize.py
│   │   │       │   │   └── normalizer.json
│   │   │       │   ├── main.py
│   │   │       │   └── schema.json
│   │   │       ├── kat_test_4/
│   │   │       │   ├── __init__.py
│   │   │       │   ├── boefje.json
│   │   │       │   └── schema.json
│   │   │       ├── main.py
│   │   │       ├── normalize.py
│   │   │       ├── normalizer.json
│   │   │       └── schema.json
│   │   ├── plugins/
│   │   │   ├── __init__.py
│   │   │   ├── test_adr_validator.py
│   │   │   ├── test_answer_parser.py
│   │   │   ├── test_bodyimage.py
│   │   │   ├── test_calvin.py
│   │   │   ├── test_cve-2023-35078.py
│   │   │   ├── test_cve-2024-6387.py
│   │   │   ├── test_cve_finding_types.py
│   │   │   ├── test_dns.py
│   │   │   ├── test_dnssec.py
│   │   │   ├── test_fierce.py
│   │   │   ├── test_generic_finding_normalizer.py
│   │   │   ├── test_leakix.py
│   │   │   ├── test_manual.py
│   │   │   ├── test_nmap.py
│   │   │   ├── test_rdns.py
│   │   │   ├── test_report_data.py
│   │   │   ├── test_scan_profiles.py
│   │   │   ├── test_security_txt.py
│   │   │   ├── test_snyk.py
│   │   │   ├── test_sslcertificate_normalizer.py
│   │   │   ├── test_testssl_sh.py
│   │   │   └── test_wappalyzer_normalizer.py
│   │   ├── test_api.py
│   │   ├── test_app.py
│   │   ├── test_job_handler.py
│   │   ├── test_models.py
│   │   ├── test_nikto_normalizer.py
│   │   └── test_tasks.py
│   └── tools/
│       ├── __init__.py
│       ├── run_boefje.py
│       ├── run_normalizer.py
│       ├── show_raw.py
│       └── upgrade_v1_17_0.py
├── bytes/
│   ├── .ci/
│   │   ├── docker-compose.yml
│   │   └── rabbitmq.conf
│   ├── .dockerignore
│   ├── .gitignore
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── bytes/
│   │   ├── __init__.py
│   │   ├── alembic.ini
│   │   ├── api/
│   │   │   ├── __init__.py
│   │   │   ├── metrics.py
│   │   │   ├── models.py
│   │   │   ├── root.py
│   │   │   └── router.py
│   │   ├── auth.py
│   │   ├── config.py
│   │   ├── database/
│   │   │   ├── __init__.py
│   │   │   ├── db.py
│   │   │   ├── db_models.py
│   │   │   ├── migrations/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── env.py
│   │   │   │   ├── script.py.mako
│   │   │   │   └── versions/
│   │   │   │       ├── 0001_initial.py
│   │   │   │       ├── 0002_drop_output_add_input.py
│   │   │   │       ├── 0003_rename_module_to_normalizer_boefje.py
│   │   │   │       ├── 0004_rename_boefje_name_to_boefje_id.py
│   │   │   │       ├── 0005_add_secure_hash_link_boefje_meta.py
│   │   │   │       ├── 0006_remove_redundant_dispatches_field.py
│   │   │   │       ├── 0007_add_raw_file_models.py
│   │   │   │       ├── 0008_point_normalizer_to_raw_file.py
│   │   │   │       ├── 0009_max_length_on_varchar_s.py
│   │   │   │       ├── 0010_longer_retrieval_link_string.py
│   │   │   │       ├── 0011_longer_normalizer_name_and_boefje_id.py
│   │   │   │       ├── 0011_rename_normalizer_name_to_normalizer_id.py
│   │   │   │       ├── 09a2929108d9_add_signing_provider_model.py
│   │   │   │       ├── 1dde0213e9fe_remove_all_hash_mime_types.py
│   │   │   │       ├── 65a39ab3e224_increase_organization_and_input_ooi_size.py
│   │   │   │       ├── __init__.py
│   │   │   │       ├── d216ad75177d_add_environment_and_runnable_hash_.py
│   │   │   │       ├── e09d8780e34b_nullable_input_ooi.py
│   │   │   │       ├── e2f76e95f1e7_.py
│   │   │   │       ├── ebc7de8be4e3_increase_organization_id_length.py
│   │   │   │       ├── ec68d3eb14b1_remove_redundant_boefje_meta_foreign.py
│   │   │   │       └── fa64454868a9_add_index_on_boefjemeta_table_.py
│   │   │   └── sql_meta_repository.py
│   │   ├── events/
│   │   │   ├── __init__.py
│   │   │   ├── events.py
│   │   │   └── manager.py
│   │   ├── models.py
│   │   ├── rabbitmq.py
│   │   ├── raw/
│   │   │   ├── __init__.py
│   │   │   ├── file_raw_repository.py
│   │   │   └── middleware.py
│   │   ├── repositories/
│   │   │   ├── __init__.py
│   │   │   ├── hash_repository.py
│   │   │   ├── meta_repository.py
│   │   │   └── raw_repository.py
│   │   ├── timestamping/
│   │   │   ├── __init__.py
│   │   │   ├── certificates/
│   │   │   │   └── freetsa.crt
│   │   │   ├── hashing.py
│   │   │   ├── in_memory.py
│   │   │   ├── pastebin.py
│   │   │   ├── provider.py
│   │   │   └── rfc3161.py
│   │   └── version.py
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── dirs
│   │   ├── install
│   │   ├── kat-bytes.service
│   │   ├── kat-bytes.sysusers
│   │   ├── postinst
│   │   ├── rules
│   │   └── triggers
│   ├── dev.logging.conf
│   ├── entrypoint.sh
│   ├── logging.conf
│   ├── packaging/
│   │   ├── deb/
│   │   │   ├── Makefile
│   │   │   └── data/
│   │   │       ├── etc/
│   │   │       │   └── kat/
│   │   │       │       ├── bytes.conf
│   │   │       │       ├── bytes.gunicorn.conf.py
│   │   │       │       └── bytes.logging.conf
│   │   │       └── usr/
│   │   │           └── bin/
│   │   │               └── update-bytes-db
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── setup.py
│   ├── sql_migrations/
│   │   ├── 0001_initial.sql
│   │   ├── 0002_drop_output_add_input.sql
│   │   ├── 0003_rename_module_to_normalizer_boefje.sql
│   │   ├── 0004_drop_dispatches_change_input_ooi.sql
│   │   ├── 0005_add_secure_hash_link_boefje_meta.sql
│   │   ├── 0006_remove_redundant_dispatches_field.sql
│   │   ├── 0007_add_raw_file_models.sql
│   │   ├── 0008_point_normalizer_to_raw.sql
│   │   ├── 0009_max_length_on_varchar_s.sql
│   │   ├── 0010_longer_retrieval_link_string.sql
│   │   ├── 0011_longer_normalizer_name_and_boefje_id.sql
│   │   ├── 0011_rename_normalizer_name_to_normalizer_id.sql
│   │   ├── 0012_65a39ab3e224_increate_organization_input_ooi_size.sql
│   │   ├── 0013_e09d8780e34b_nullable_input_ooi.sql
│   │   ├── 0014_ebc7de8be4e3_increase_organization_id_length.sql
│   │   ├── 0015_fa64454868a9_add_indices.sql
│   │   ├── 0016_ec68d3eb14b1_remove_redundant_boefje_meta_foreign.sql
│   │   ├── 0017_09a2929108d9_add_signing_provider_model.sql
│   │   ├── 0018_d216ad75177d_add_environment_and_runnable_hash.sql
│   │   └── 0019_1dde0213e9fe_remove_all_hash_mime_types.sql
│   └── tests/
│       ├── __init__.py
│       ├── client.py
│       ├── conftest.py
│       ├── integration/
│       │   ├── __init__.py
│       │   ├── requests/
│       │   │   └── calvin.http
│       │   ├── test_bytes_api.py
│       │   ├── test_event.py
│       │   ├── test_hash_service.py
│       │   ├── test_meta_repository.py
│       │   ├── test_migrations.py
│       │   └── test_timestamper.py
│       ├── loading.py
│       ├── seed_database.py
│       ├── stubs/
│       │   ├── dummy.json
│       │   ├── freetsa.crt
│       │   └── login-request.json
│       └── unit/
│           ├── __init__.py
│           ├── test_api.py
│           ├── test_auth.py
│           ├── test_context_mapping.py
│           ├── test_hash.py
│           └── test_raw_repository.py
├── cveapi/
│   ├── Dockerfile
│   ├── Makefile
│   ├── cveapi.py
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── kat-cveapi.service
│   │   ├── kat-cveapi.sysusers
│   │   ├── kat-cveapi.timer
│   │   └── rules
│   ├── entrypoint.sh
│   ├── packaging/
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   └── requirements.txt
├── docker-compose.release-example.yml
├── docker-compose.yml
├── docs/
│   ├── settings-doc-templates/
│   │   └── markdown.jinja
│   └── source/
│       ├── _static/
│       │   └── openkat.css
│       ├── about-openkat/
│       │   ├── index.rst
│       │   ├── release-notes/
│       │   │   ├── 1.10.rst
│       │   │   ├── 1.11.rst
│       │   │   ├── 1.12.rst
│       │   │   ├── 1.13.rst
│       │   │   ├── 1.14.rst
│       │   │   ├── 1.15.rst
│       │   │   ├── 1.16.rst
│       │   │   ├── 1.17.rst
│       │   │   ├── 1.18.rst
│       │   │   ├── 1.19.rst
│       │   │   ├── 1.20.rst
│       │   │   ├── 1.21.rst
│       │   │   ├── 1.5.rst
│       │   │   ├── 1.6.rst
│       │   │   ├── 1.7.rst
│       │   │   ├── 1.8.rst
│       │   │   ├── 1.9.rst
│       │   │   └── index.rst
│       │   └── what-is-openkat.rst
│       ├── conf.py
│       ├── developer-documentation/
│       │   ├── basic-principles/
│       │   │   ├── bits.rst
│       │   │   ├── boefjes-whiskers-bits.rst
│       │   │   ├── boefjes.rst
│       │   │   ├── index.rst
│       │   │   ├── introduction.rst
│       │   │   ├── modules.rst
│       │   │   ├── normalizers.rst
│       │   │   ├── origin-types.rst
│       │   │   ├── questions-and-configs.rst
│       │   │   └── verify-timestamps.rst
│       │   ├── boefjes-runner.md
│       │   ├── boefjes.md
│       │   ├── bytes.md
│       │   ├── contributor/
│       │   │   ├── guidelines/
│       │   │   │   ├── contributions.rst
│       │   │   │   ├── development.rst
│       │   │   │   ├── feature_flow.md
│       │   │   │   ├── ideas.rst
│       │   │   │   ├── index.rst
│       │   │   │   ├── management.rst
│       │   │   │   └── security.rst
│       │   │   ├── index.rst
│       │   │   ├── intro.rst
│       │   │   ├── templates/
│       │   │   │   ├── bug_report_template.md
│       │   │   │   ├── feature_request_template.md
│       │   │   │   ├── index.rst
│       │   │   │   ├── pull_request_template_author.md
│       │   │   │   ├── pull_request_template_review_code.md
│       │   │   │   └── pull_request_template_review_qa.md
│       │   │   └── ux-design/
│       │   │       ├── figma.rst
│       │   │       └── index.rst
│       │   ├── development-tutorial/
│       │   │   ├── creating-bit.md
│       │   │   ├── creating-boefje.rst
│       │   │   ├── creating-model.md
│       │   │   ├── creating-normalizer.md
│       │   │   ├── creating-report.md
│       │   │   ├── index.rst
│       │   │   └── testing-boefje.md
│       │   ├── index.rst
│       │   ├── mula.md
│       │   ├── normalisers-runner.md
│       │   ├── octopoes-models.rst
│       │   ├── octopoes-research.rst
│       │   ├── octopoes.md
│       │   ├── qa-test-plan.rst
│       │   ├── quick-start.rst
│       │   ├── reports.md
│       │   └── rocky.md
│       ├── index.rst
│       ├── installation-and-deployment/
│       │   ├── adding-proxy-to-openkat.rst
│       │   ├── cveapi.rst
│       │   ├── debugging-troubleshooting.rst
│       │   ├── developer-environment.rst
│       │   ├── environment-settings/
│       │   │   ├── index.rst
│       │   │   └── rocky.md
│       │   ├── events-and-logging.rst
│       │   ├── external-authentication.rst
│       │   ├── faq.rst
│       │   ├── gitpod.rst
│       │   ├── hardening.rst
│       │   ├── index.rst
│       │   ├── install.rst
│       │   ├── installation-options
│       │   ├── local-install.rst
│       │   ├── production-debian-environment.rst
│       │   ├── production-docker-environment.rst
│       │   ├── s3-buckets.rst
│       │   ├── scripts.rst
│       │   ├── separate-boefje-workers.rst
│       │   ├── users-and-organizations.rst
│       │   └── windows-install.rst
│       └── user-manual/
│           ├── basic-concepts/
│           │   ├── index.rst
│           │   ├── objects-and-recursion.rst
│           │   └── scan-levels-and-indemnification.rst
│           ├── getting-started/
│           │   ├── generate-report.rst
│           │   ├── index.rst
│           │   ├── introduction.rst
│           │   ├── login-and-registration.rst
│           │   ├── onboarding.rst
│           │   └── start-scanning.rst
│           ├── glossary.rst
│           ├── index.rst
│           └── navigation/
│               ├── crisis-room.rst
│               ├── findings.rst
│               ├── index.rst
│               ├── katalogus.rst
│               ├── members.rst
│               ├── objects.rst
│               ├── overview.rst
│               ├── reports.rst
│               ├── settings.rst
│               ├── tasks.rst
│               └── user-settings.rst
├── init-user-db.sh
├── monitoring/
│   ├── config.alloy
│   ├── grafana-provisioning/
│   │   ├── datasources/
│   │   │   └── pyroscope.yml
│   │   └── plugins/
│   │       └── explore-profiles.yml
│   ├── jaeger.yml
│   └── prometheus.yml
├── mula/
│   ├── .ci/
│   │   ├── docker-compose.yml
│   │   └── rabbitmq.conf
│   ├── .dockerignore
│   ├── .gitignore
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── install
│   │   ├── kat-mula.service
│   │   ├── kat-mula.sysusers
│   │   ├── postinst
│   │   ├── rules
│   │   └── triggers
│   ├── docs/
│   │   ├── api.md
│   │   ├── architecture.md
│   │   ├── configuration.md
│   │   ├── extending.md
│   │   └── metrics.md
│   ├── entrypoint.sh
│   ├── logging.json
│   ├── logging.prod.json
│   ├── packaging/
│   │   ├── deb/
│   │   │   ├── Makefile
│   │   │   └── data/
│   │   │       ├── etc/
│   │   │       │   └── kat/
│   │   │       │       ├── mula.conf
│   │   │       │       └── mula.logging.json
│   │   │       └── usr/
│   │   │           └── bin/
│   │   │               └── update-mula-db
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── scheduler/
│   │   ├── __init__.py
│   │   ├── __main__.py
│   │   ├── app.py
│   │   ├── clients/
│   │   │   ├── __init__.py
│   │   │   ├── amqp/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── listeners.py
│   │   │   │   ├── raw_data.py
│   │   │   │   └── scan_profile.py
│   │   │   ├── connector.py
│   │   │   ├── errors.py
│   │   │   └── http/
│   │   │       ├── __init__.py
│   │   │       ├── client.py
│   │   │       ├── external/
│   │   │       │   ├── __init__.py
│   │   │       │   ├── bytes.py
│   │   │       │   ├── katalogus.py
│   │   │       │   ├── octopoes.py
│   │   │       │   └── rocky.py
│   │   │       └── service.py
│   │   ├── config/
│   │   │   ├── __init__.py
│   │   │   └── settings.py
│   │   ├── context/
│   │   │   ├── __init__.py
│   │   │   └── context.py
│   │   ├── models/
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── boefje.py
│   │   │   ├── errors.py
│   │   │   ├── events.py
│   │   │   ├── health.py
│   │   │   ├── normalizer.py
│   │   │   ├── ooi.py
│   │   │   ├── organisation.py
│   │   │   ├── plugin.py
│   │   │   ├── queue.py
│   │   │   ├── raw_data.py
│   │   │   ├── schedule.py
│   │   │   ├── scheduler.py
│   │   │   └── task.py
│   │   ├── schedulers/
│   │   │   ├── __init__.py
│   │   │   ├── errors.py
│   │   │   ├── queue/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── errors.py
│   │   │   │   └── pq.py
│   │   │   ├── rankers/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.py
│   │   │   │   ├── normalizer.py
│   │   │   │   └── ranker.py
│   │   │   ├── scheduler.py
│   │   │   └── schedulers/
│   │   │       ├── __init__.py
│   │   │       ├── boefje.py
│   │   │       ├── normalizer.py
│   │   │       └── report.py
│   │   ├── server/
│   │   │   ├── __init__.py
│   │   │   ├── errors.py
│   │   │   ├── handlers/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── health.py
│   │   │   │   ├── metrics.py
│   │   │   │   ├── root.py
│   │   │   │   ├── schedulers.py
│   │   │   │   ├── schedules.py
│   │   │   │   └── tasks.py
│   │   │   ├── schemas/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── health.py
│   │   │   │   ├── schedule.py
│   │   │   │   ├── scheduler.py
│   │   │   │   └── task.py
│   │   │   ├── server.py
│   │   │   └── utils/
│   │   │       ├── __init__.py
│   │   │       └── pagination.py
│   │   ├── storage/
│   │   │   ├── __init__.py
│   │   │   ├── connection.py
│   │   │   ├── errors.py
│   │   │   ├── filters/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── casting.py
│   │   │   │   ├── comparison.py
│   │   │   │   ├── errors.py
│   │   │   │   ├── filters.py
│   │   │   │   ├── functions.py
│   │   │   │   └── operators.py
│   │   │   ├── migrations/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── alembic.ini
│   │   │   │   ├── env.py
│   │   │   │   ├── script.py.mako
│   │   │   │   └── versions/
│   │   │   │       ├── 0001_initial_migration.py
│   │   │   │       ├── 0002_update_tasks.py
│   │   │   │       ├── 0003_add_type_field_to_tasks.py
│   │   │   │       ├── 0004_add_server_default.py
│   │   │   │       ├── 0005_size_limit_hash.py
│   │   │   │       ├── 0006_add_jsonb_fields_add_jsonb_fields.py
│   │   │   │       ├── 0007_add_cancelled_status_for_tasks.py
│   │   │   │       ├── 0008_add_task_schedule.py
│   │   │   │       ├── 0009_add_organisation.py
│   │   │   │       ├── 0010_add_indices.py
│   │   │   │       ├── 0011_add_more_indexes.py
│   │   │   │       └── __init__.py
│   │   │   ├── stores/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── pq.py
│   │   │   │   ├── schedule.py
│   │   │   │   └── task.py
│   │   │   └── utils.py
│   │   ├── utils/
│   │   │   ├── __init__.py
│   │   │   ├── cron.py
│   │   │   ├── datastore.py
│   │   │   ├── dict_utils.py
│   │   │   ├── errors.py
│   │   │   ├── functions.py
│   │   │   └── thread.py
│   │   └── version.py
│   ├── scripts/
│   │   ├── .gitignore
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── __init__.py
│   │   ├── benchmark.py
│   │   ├── data.csv.example
│   │   └── load.py
│   ├── setup.py
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── factories/
│   │   │   ├── __init__.py
│   │   │   ├── boefje.py
│   │   │   ├── normalizer.py
│   │   │   ├── ooi.py
│   │   │   ├── organisation.py
│   │   │   ├── plugin.py
│   │   │   └── raw_data.py
│   │   ├── integration/
│   │   │   ├── __init__.py
│   │   │   ├── test_api.py
│   │   │   ├── test_app.py
│   │   │   ├── test_boefje_scheduler.py
│   │   │   ├── test_clients.py
│   │   │   ├── test_listeners.py
│   │   │   ├── test_normalizer_scheduler.py
│   │   │   ├── test_pq_store.py
│   │   │   ├── test_report_scheduler.py
│   │   │   ├── test_schedule_store.py
│   │   │   ├── test_scheduler.py
│   │   │   └── test_task_store.py
│   │   ├── mocks/
│   │   │   ├── __init__.py
│   │   │   ├── item.py
│   │   │   ├── listener.py
│   │   │   ├── queue.py
│   │   │   ├── ranker.py
│   │   │   ├── scheduler.py
│   │   │   ├── services.py
│   │   │   └── task.py
│   │   ├── simulation/
│   │   │   ├── __init__.py
│   │   │   └── test_simulation.py
│   │   ├── unit/
│   │   │   ├── __init__.py
│   │   │   ├── test_filter.py
│   │   │   ├── test_queue.py
│   │   │   ├── test_rankers.py
│   │   │   └── test_utils.py
│   │   └── utils/
│   │       ├── __init__.py
│   │       ├── functions.py
│   │       ├── json.py
│   │       └── memory.py
│   └── whitelist.py
├── octopoes/
│   ├── .ci/
│   │   ├── docker-compose.yml
│   │   ├── mock_bits/
│   │   │   └── url_classification_mock/
│   │   │       ├── __init__.py
│   │   │       ├── bit.py
│   │   │       └── url_classification_mock.py
│   │   └── wiremock/
│   │       └── mappings/
│   │           └── organisations.json
│   ├── .dockerignore
│   ├── .editorconfig
│   ├── .gitignore
│   ├── AUTHORS
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── bits/
│   │   ├── __init__.py
│   │   ├── ask_disallowed_domains/
│   │   │   ├── __init__.py
│   │   │   ├── ask_disallowed_domains.py
│   │   │   ├── bit.py
│   │   │   └── question_schema.json
│   │   ├── ask_port_specification/
│   │   │   ├── __init__.py
│   │   │   ├── ask_port_specification.py
│   │   │   ├── bit.py
│   │   │   └── question_schema.json
│   │   ├── ask_url_params_to_ignore/
│   │   │   ├── __init__.py
│   │   │   ├── ask_url_params_to_ignore.py
│   │   │   ├── bit.py
│   │   │   └── question_schema.json
│   │   ├── check_csp_header/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── check_csp_header.py
│   │   ├── check_cve_2021_41773/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── check_cve_2021_41773.py
│   │   ├── check_hsts_header/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── check_hsts_header.py
│   │   ├── cipher_classification/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   ├── cipher_classification.py
│   │   │   └── list-ciphers-openssl-with-finding-type.csv
│   │   ├── default_findingtype_risk/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── default_findingtype_risk.py
│   │   ├── definitions.py
│   │   ├── disallowed_csp_hostnames/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── disallowed_csp_hostnames.py
│   │   ├── dns_alias_resolving/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── dns_alias_resolving.py
│   │   ├── dns_resolving/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── dns_resolving.py
│   │   ├── domain_owner_verification/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── domain_owner_verification.py
│   │   ├── expiring_certificate/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── expiring_certificate.py
│   │   ├── https_availability/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── https_availability.py
│   │   ├── https_redirect/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── https_redirect.py
│   │   ├── internetnl/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── internetnl.py
│   │   ├── ipv6_nameservers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── ipv6_nameservers.py
│   │   ├── ipv6_webservers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── ipv6_webservers.py
│   │   ├── missing_caa/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_caa.py
│   │   ├── missing_dkim/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_dkim.py
│   │   ├── missing_dmarc/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_dmarc.py
│   │   ├── missing_headers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_headers.py
│   │   ├── missing_spf/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_spf.py
│   │   ├── nxdomain_flag/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── nxdomain_flag.py
│   │   ├── nxdomain_header_flag/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── nxdomain_header_flag.py
│   │   ├── oois_in_headers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── oois_in_headers.py
│   │   ├── port_classification_ip/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── port_classification_ip.py
│   │   ├── port_common/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── port_common.py
│   │   ├── resource_discovery/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── resource_discovery.py
│   │   ├── retire_js/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   ├── retire_js.py
│   │   │   └── retirejs.json
│   │   ├── runner.py
│   │   ├── spf_discovery/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   ├── internetnl_spf_parser.py
│   │   │   └── spf_discovery.py
│   │   ├── ssl_certificate_hostname/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── ssl_certificate_hostname.py
│   │   ├── two_ipv6_nameservers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── two_ipv6_nameservers.py
│   │   ├── url_classification/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── url_classification.py
│   │   ├── url_discovery/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── url_discovery.py
│   │   └── website_discovery/
│   │       ├── __init__.py
│   │       ├── bit.py
│   │       └── website_discovery.py
│   ├── conftest.py
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── install
│   │   ├── kat-octopoes.kat-octopoes-worker.service
│   │   ├── kat-octopoes.kat-octopoes.service
│   │   ├── kat-octopoes.sysusers
│   │   ├── postinst
│   │   ├── rules
│   │   └── triggers
│   ├── docs/
│   │   └── v3.md
│   ├── entrypoint.sh
│   ├── logging.yml
│   ├── octopoes/
│   │   ├── __init__.py
│   │   ├── api/
│   │   │   ├── __init__.py
│   │   │   ├── api.py
│   │   │   ├── bulk_router.py
│   │   │   ├── models.py
│   │   │   └── router.py
│   │   ├── config/
│   │   │   ├── __init__.py
│   │   │   ├── celery.py
│   │   │   └── settings.py
│   │   ├── connector/
│   │   │   ├── __init__.py
│   │   │   ├── katalogus.py
│   │   │   └── octopoes.py
│   │   ├── core/
│   │   │   ├── __init__.py
│   │   │   ├── app.py
│   │   │   └── service.py
│   │   ├── events/
│   │   │   ├── __init__.py
│   │   │   ├── events.py
│   │   │   └── manager.py
│   │   ├── models/
│   │   │   ├── __init__.py
│   │   │   ├── datetime.py
│   │   │   ├── exception.py
│   │   │   ├── explanation.py
│   │   │   ├── ooi/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── certificate.py
│   │   │   │   ├── config.py
│   │   │   │   ├── dns/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── records.py
│   │   │   │   │   └── zone.py
│   │   │   │   ├── email_security.py
│   │   │   │   ├── findings.py
│   │   │   │   ├── geography.py
│   │   │   │   ├── monitoring.py
│   │   │   │   ├── network.py
│   │   │   │   ├── question.py
│   │   │   │   ├── reports.py
│   │   │   │   ├── scans.py
│   │   │   │   ├── service.py
│   │   │   │   ├── software.py
│   │   │   │   └── web.py
│   │   │   ├── origin.py
│   │   │   ├── pagination.py
│   │   │   ├── path.py
│   │   │   ├── persistence.py
│   │   │   ├── transaction.py
│   │   │   ├── tree.py
│   │   │   └── types.py
│   │   ├── repositories/
│   │   │   ├── __init__.py
│   │   │   ├── ooi_repository.py
│   │   │   ├── origin_parameter_repository.py
│   │   │   ├── origin_repository.py
│   │   │   ├── repository.py
│   │   │   └── scan_profile_repository.py
│   │   ├── tasks/
│   │   │   ├── __init__.py
│   │   │   ├── app.py
│   │   │   ├── scanprofiles.py
│   │   │   └── tasks.py
│   │   ├── types.py
│   │   ├── version.py
│   │   └── xtdb/
│   │       ├── __init__.py
│   │       ├── client.py
│   │       ├── exceptions.py
│   │       ├── query.py
│   │       ├── query_builder.py
│   │       └── related_field_generator.py
│   ├── packaging/
│   │   ├── deb/
│   │   │   ├── Makefile
│   │   │   └── data/
│   │   │       └── etc/
│   │   │           └── kat/
│   │   │               ├── octopoes.conf
│   │   │               ├── octopoes.gunicorn.conf.py
│   │   │               └── octopoes.logging.yml
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── prod.logging.yml
│   ├── pyproject.toml
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── setup.py
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── fixtures/
│   │   │   ├── calvin_output_1.json
│   │   │   ├── calvin_output_2.json
│   │   │   ├── normalizer_output.json
│   │   │   ├── normalizer_output_config.json
│   │   │   ├── normalizer_output_empty.json
│   │   │   ├── normalizer_output_http.json
│   │   │   ├── normalizer_output_nxdomain.json
│   │   │   ├── regular_manual_declaration.json
│   │   │   └── regular_manual_declaration_with_null_values.json
│   │   ├── integration/
│   │   │   ├── __init__.py
│   │   │   ├── test_api_connector.py
│   │   │   ├── test_io.py
│   │   │   ├── test_ooi_deletion.py
│   │   │   ├── test_ooi_repository.py
│   │   │   ├── test_unicode.py
│   │   │   └── test_xtdb_client.py
│   │   ├── mocks/
│   │   │   ├── __init__.py
│   │   │   ├── mock_ooi_types.py
│   │   │   └── mock_ooi_types_abstract.py
│   │   ├── robot/
│   │   │   ├── 01_scan_profiles.robot
│   │   │   ├── 02_list_objects.robot
│   │   │   ├── 03_deletion_propagation.robot
│   │   │   ├── 04_save_declaration.robot
│   │   │   ├── 05_bits.robot
│   │   │   ├── 06_scan_profile_inheritance.robot
│   │   │   ├── 07_bit_configs.robot
│   │   │   ├── 07_rerun_bits.robot
│   │   │   ├── 08_findings.robot
│   │   │   ├── CeleryMonitor.py
│   │   │   ├── __init__.py
│   │   │   └── robot.resource
│   │   ├── test_api.py
│   │   ├── test_bit_ask_ports.py
│   │   ├── test_bit_cipher.py
│   │   ├── test_bit_csp_header.py
│   │   ├── test_bit_default_findingtype_risk.py
│   │   ├── test_bit_domain_verification.py
│   │   ├── test_bit_expiring_certificate.py
│   │   ├── test_bit_missing_headers.py
│   │   ├── test_bit_ports.py
│   │   ├── test_bit_spf_discovery.py
│   │   ├── test_bits.py
│   │   ├── test_disallowed_csp_hostnames.py
│   │   ├── test_event_manager.py
│   │   ├── test_octopoes_service.py
│   │   ├── test_ooi.py
│   │   ├── test_ooi_repository.py
│   │   ├── test_origin_repository.py
│   │   ├── test_path.py
│   │   ├── test_query.py
│   │   ├── test_query_builder_new.py
│   │   ├── test_recalculate_scan_profiles.py
│   │   ├── test_reference.py
│   │   ├── test_reference_node.py
│   │   ├── test_scan_profile_repository.py
│   │   └── test_type_analysis.py
│   └── tools/
│       ├── __init__.py
│       ├── analyze-bit-metric.py
│       ├── rename-origin-method.py
│       ├── run_bit.py
│       ├── xtdb-cli.py
│       └── xtdb_client.py
├── packaging/
│   ├── debian12/
│   │   └── Dockerfile
│   └── ubuntu22.04/
│       └── Dockerfile
├── pyproject.toml
├── requirements.txt
├── rfd/
│   ├── 0001-rfd.md
│   ├── 0002-code-of-conduct.md
│   ├── 0003-deduplication.md
│   ├── README.md
│   ├── assets/
│   │   └── .gitkeep
│   └── prototypes/
│       └── prototype.md
├── rocky/
│   ├── .ci/
│   │   └── docker-compose.yml
│   ├── .dockerignore
│   ├── .editorconfig
│   ├── .gitignore
│   ├── .nvmrc
│   ├── .parcelrc
│   ├── .prettierignore
│   ├── .sassrc
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── OOI_database_seed.json
│   ├── account/
│   │   ├── __init__.py
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── forms/
│   │   │   ├── __init__.py
│   │   │   ├── account_setup.py
│   │   │   ├── login.py
│   │   │   ├── organization.py
│   │   │   ├── password_reset.py
│   │   │   └── token.py
│   │   ├── management/
│   │   │   ├── __init__.py
│   │   │   └── commands/
│   │   │       ├── __init__.py
│   │   │       └── create_authtoken.py
│   │   ├── migrations/
│   │   │   ├── 0001_initial.py
│   │   │   ├── 0001_squashed_0004_authtoken_authtoken_unique_name.py
│   │   │   ├── 0002_remove_first_last_name.py
│   │   │   ├── 0003_alter_katuser_full_name.py
│   │   │   ├── 0004_authtoken_authtoken_unique_name.py
│   │   │   ├── 0005_katuser_clearance_level.py
│   │   │   └── __init__.py
│   │   ├── mixins.py
│   │   ├── models.py
│   │   ├── templates/
│   │   │   ├── account_detail.html
│   │   │   ├── password_reset.html
│   │   │   ├── password_reset_confirm.html
│   │   │   ├── password_reset_done.html
│   │   │   ├── password_reset_email.html
│   │   │   ├── password_reset_subject.txt
│   │   │   ├── recover_email.html
│   │   │   ├── registration_email.html
│   │   │   └── registration_subject.txt
│   │   ├── urls.py
│   │   ├── validators.py
│   │   └── views/
│   │       ├── __init__.py
│   │       ├── account.py
│   │       ├── login.py
│   │       ├── password_reset.py
│   │       └── recover_email.py
│   ├── assets/
│   │   ├── css/
│   │   │   ├── abstracts/
│   │   │   │   ├── alert-colors.scss
│   │   │   │   └── mixins.scss
│   │   │   ├── components/
│   │   │   │   ├── action-buttons.scss
│   │   │   │   ├── block-indented.scss
│   │   │   │   ├── button-plain.scss
│   │   │   │   ├── cat-loader.scss
│   │   │   │   ├── cat-paw-loader.scss
│   │   │   │   ├── chapter-numbering.scss
│   │   │   │   ├── cytoscape.scss
│   │   │   │   ├── dashboard.scss
│   │   │   │   ├── destructive.scss
│   │   │   │   ├── dropdown-form.scss
│   │   │   │   ├── dropdown-list.scss
│   │   │   │   ├── dropdown.scss
│   │   │   │   ├── filter.scss
│   │   │   │   ├── footer-logo.scss
│   │   │   │   ├── footer.scss
│   │   │   │   ├── form-checkbox.scss
│   │   │   │   ├── form-inline.scss
│   │   │   │   ├── form.scss
│   │   │   │   ├── header-navigation.scss
│   │   │   │   ├── hover-block.scss
│   │   │   │   ├── input-link.scss
│   │   │   │   ├── introduction.scss
│   │   │   │   ├── language.scss
│   │   │   │   ├── layout-tiles-width-1.scss
│   │   │   │   ├── layout.scss
│   │   │   │   ├── messages.scss
│   │   │   │   ├── notifications.scss
│   │   │   │   ├── ooi-summary.scss
│   │   │   │   ├── page-meta.scss
│   │   │   │   ├── plugins.scss
│   │   │   │   ├── print.scss
│   │   │   │   ├── print_report.scss
│   │   │   │   ├── qr-code.scss
│   │   │   │   ├── report-name-table.scss
│   │   │   │   ├── report.scss
│   │   │   │   ├── risk-level-indicator.scss
│   │   │   │   ├── scan-level-indicator.scss
│   │   │   │   ├── select.scss
│   │   │   │   ├── state-tag.scss
│   │   │   │   ├── state-tags.scss
│   │   │   │   ├── stepper-variables.scss
│   │   │   │   ├── stepper.scss
│   │   │   │   ├── sticky-column.scss
│   │   │   │   ├── sticky.scss
│   │   │   │   ├── system-tag.scss
│   │   │   │   ├── table-sortable.scss
│   │   │   │   ├── table-state-icons.scss
│   │   │   │   ├── table.scss
│   │   │   │   ├── toggle.scss
│   │   │   │   ├── toolbar.scss
│   │   │   │   ├── tree-tables.scss
│   │   │   │   ├── user-icon.scss
│   │   │   │   └── wait-text.scss
│   │   │   ├── helpers/
│   │   │   │   ├── align-right.scss
│   │   │   │   ├── is-hidden.scss
│   │   │   │   └── uc-first.scss
│   │   │   ├── main.scss
│   │   │   ├── manon-components.scss
│   │   │   ├── report.scss
│   │   │   ├── themes/
│   │   │   │   └── soft/
│   │   │   │       ├── fonts/
│   │   │   │       │   ├── fonts.scss
│   │   │   │       │   ├── fredoka/
│   │   │   │       │   │   ├── OFL.txt
│   │   │   │       │   │   └── fredoka.scss
│   │   │   │       │   ├── kat-icons/
│   │   │   │       │   │   └── kat-icons.scss
│   │   │   │       │   ├── open-sans/
│   │   │   │       │   │   ├── OFL.txt
│   │   │   │       │   │   └── open-sans.scss
│   │   │   │       │   └── tabler-icons/
│   │   │   │       │       ├── LICENSE
│   │   │   │       │       └── tabler-icons.scss
│   │   │   │       ├── fundamentals/
│   │   │   │       │   ├── body-text-set.scss
│   │   │   │       │   ├── border-radii.scss
│   │   │   │       │   ├── colors.scss
│   │   │   │       │   ├── tag-colors.scss
│   │   │   │       │   └── tags-6-3.scss
│   │   │   │       ├── manon/
│   │   │   │       │   ├── accordion.scss
│   │   │   │       │   ├── application-base.scss
│   │   │   │       │   ├── article-content-wrapper.scss
│   │   │   │       │   ├── article.scss
│   │   │   │       │   ├── breadcrumb-bar.scss
│   │   │   │       │   ├── button-destructive.scss
│   │   │   │       │   ├── button-ghost.scss
│   │   │   │       │   ├── button-icon.scss
│   │   │   │       │   ├── button.scss
│   │   │   │       │   ├── checkbox.scss
│   │   │   │       │   ├── code-base.scss
│   │   │   │       │   ├── code-block.scss
│   │   │   │       │   ├── collapsible.scss
│   │   │   │       │   ├── collapsing-element.scss
│   │   │   │       │   ├── de-emphasized.scss
│   │   │   │       │   ├── description-list.scss
│   │   │   │       │   ├── emphasized.scss
│   │   │   │       │   ├── expando-rows.scss
│   │   │   │       │   ├── filter.scss
│   │   │   │       │   ├── footer.scss
│   │   │   │       │   ├── form-button.scss
│   │   │   │       │   ├── form-fieldset.scss
│   │   │   │       │   ├── form-help.scss
│   │   │   │       │   ├── form-inline.scss
│   │   │   │       │   ├── form-radio.scss
│   │   │   │       │   ├── form.scss
│   │   │   │       │   ├── header-navigation-collapsible.scss
│   │   │   │       │   ├── header-navigation-content-wrapper.scss
│   │   │   │       │   ├── header-navigation-link-active.scss
│   │   │   │       │   ├── header-navigation-link.scss
│   │   │   │       │   ├── header-navigation.scss
│   │   │   │       │   ├── headings-base-set.scss
│   │   │   │       │   ├── headings.scss
│   │   │   │       │   ├── hero.scss
│   │   │   │       │   ├── icon.scss
│   │   │   │       │   ├── image-square.scss
│   │   │   │       │   ├── language-selector-list.scss
│   │   │   │       │   ├── layout-column-4.scss
│   │   │   │       │   ├── layout-form.scss
│   │   │   │       │   ├── layout-set.scss
│   │   │   │       │   ├── layout.scss
│   │   │   │       │   ├── link.scss
│   │   │   │       │   ├── login-meta.scss
│   │   │   │       │   ├── logo.scss
│   │   │   │       │   ├── main.scss
│   │   │   │       │   ├── manon-variables.scss
│   │   │   │       │   ├── max-line-length.scss
│   │   │   │       │   ├── navigation-collapsible.scss
│   │   │   │       │   ├── navigation.scss
│   │   │   │       │   ├── nota-bene.scss
│   │   │   │       │   ├── notification-block.scss
│   │   │   │       │   ├── notification-colors.scss
│   │   │   │       │   ├── notification-paragraph.scss
│   │   │   │       │   ├── section-content-wrapper.scss
│   │   │   │       │   ├── section.scss
│   │   │   │       │   ├── sidemenu.scss
│   │   │   │       │   ├── spacing.scss
│   │   │   │       │   ├── spot-large.scss
│   │   │   │       │   ├── table.scss
│   │   │   │       │   ├── tabs.scss
│   │   │   │       │   ├── tags.scss
│   │   │   │       │   ├── text-colors.scss
│   │   │   │       │   └── tile.scss
│   │   │   │       ├── report.scss
│   │   │   │       └── soft.scss
│   │   │   └── vendor_overrides/
│   │   │       ├── graph-override.scss
│   │   │       ├── manon/
│   │   │       │   ├── button-icon-only.scss
│   │   │       │   ├── display-toggle.scss
│   │   │       │   ├── dl.scss
│   │   │       │   ├── form-fieldset-required.scss
│   │   │       │   ├── form-radio.scss
│   │   │       │   ├── image-square.scss
│   │   │       │   ├── layout-fifty-fifty.scss
│   │   │       │   ├── layout-form.scss
│   │   │       │   ├── nested-section.scss
│   │   │       │   ├── notification.scss
│   │   │       │   ├── table.scss
│   │   │       │   ├── tabs.scss
│   │   │       │   └── tile.scss
│   │   │       ├── two-factor.scss
│   │   │       └── weasyprint/
│   │   │           ├── accordion.scss
│   │   │           ├── application-base.scss
│   │   │           ├── button.scss
│   │   │           ├── chapter-numbers.scss
│   │   │           ├── description-list.scss
│   │   │           ├── headings.scss
│   │   │           ├── layout-centered.scss
│   │   │           ├── pdf-overrides.scss
│   │   │           ├── report-manon-components.scss
│   │   │           ├── section-wrapper.scss
│   │   │           ├── section.scss
│   │   │           ├── table-of-contents.scss
│   │   │           ├── table.scss
│   │   │           ├── tile.scss
│   │   │           ├── toolbar.scss
│   │   │           ├── ul.scss
│   │   │           └── visually-hidden.scss
│   │   ├── js/
│   │   │   ├── app.js
│   │   │   ├── autoSubmit.js
│   │   │   ├── checkboxToggler.js
│   │   │   ├── choiceToggle.js
│   │   │   ├── components.js
│   │   │   ├── dashboard.js
│   │   │   ├── dropdown.js
│   │   │   ├── enableButtonTimeout.js
│   │   │   ├── grabSelectionOnModalOpen.js
│   │   │   ├── imports/
│   │   │   │   ├── graph.js
│   │   │   │   ├── manon.js
│   │   │   │   └── utils.js
│   │   │   ├── jsonSchemaToForm.js
│   │   │   ├── pluginToggler.js
│   │   │   ├── renameReports.js
│   │   │   ├── report.js
│   │   │   ├── reportActionForms.js
│   │   │   ├── sidemenuOl.js
│   │   │   ├── stringHelpers.js
│   │   │   ├── tabs.js
│   │   │   ├── taskDetails.js
│   │   │   └── utils.js
│   │   └── vendors/
│   │       └── graph/
│   │           ├── css/
│   │           │   └── graph-d3.css
│   │           └── js/
│   │               ├── graph-d3.js
│   │               └── graph-render.js
│   ├── components/
│   │   ├── __init__.py
│   │   ├── main.scss
│   │   └── modal/
│   │       ├── README.md
│   │       ├── __init__.py
│   │       ├── modal.py
│   │       ├── script.js
│   │       ├── style.scss
│   │       └── template.html
│   ├── crisis_room/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── forms.py
│   │   ├── management/
│   │   │   ├── __init__.py
│   │   │   └── commands/
│   │   │       ├── __init__.py
│   │   │       ├── dashboards.py
│   │   │       └── recipe_seeder.json
│   │   ├── migrations/
│   │   │   ├── 0001_initial.py
│   │   │   ├── 0002_create_findings_dashboards.py
│   │   │   ├── 0003_alter_dashboarddata_unique_together_and_more.py
│   │   │   ├── 0004_change_name_findings_dashboard.py
│   │   │   ├── 0005_add_dashboard_permissions_to_groups.py
│   │   │   ├── 0006_rename_dashboarddata_dashboarditem.py
│   │   │   ├── 0007_alter_dashboarditem_source.py
│   │   │   └── __init__.py
│   │   ├── models.py
│   │   ├── templates/
│   │   │   ├── crisis_room.html
│   │   │   ├── crisis_room_dashboards.html
│   │   │   ├── crisis_room_findings.html
│   │   │   ├── crisis_room_header.html
│   │   │   ├── organization_crisis_room.html
│   │   │   ├── organization_crisis_room_dashboard_items.html
│   │   │   ├── organization_crisis_room_header.html
│   │   │   └── partials/
│   │   │       ├── crisis_room_findings_table.html
│   │   │       ├── dashboard_finding_list.html
│   │   │       ├── dashboard_finding_list_table.html
│   │   │       ├── dashboard_item_action_button.html
│   │   │       ├── dashboard_ooi_list.html
│   │   │       ├── dashboard_ooi_list_table.html
│   │   │       ├── delete_dashboard_item_modal.html
│   │   │       ├── delete_dashboard_modal.html
│   │   │       ├── new_dashboard_item_action_button.html
│   │   │       ├── new_dashboard_item_explanation_modal.html
│   │   │       ├── new_dashboard_item_modal.html
│   │   │       ├── new_dashboard_item_modal_report_section.html
│   │   │       ├── new_dashboard_modal.html
│   │   │       ├── report_dashboard_item_filter.html
│   │   │       └── report_section_action_button.html
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   └── crisis_room.py
│   │   ├── urls.py
│   │   └── views.py
│   ├── database.env-dist
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── install
│   │   ├── kat-rocky-worker.service
│   │   ├── kat-rocky.service
│   │   ├── kat-rocky.sysusers
│   │   ├── postinst
│   │   ├── postrm
│   │   ├── rules
│   │   └── triggers
│   ├── docs/
│   │   └── reports.md
│   ├── entrypoint.sh
│   ├── fmea/
│   │   ├── __init__.py
│   │   └── migrations/
│   │       ├── 0001_initial.py
│   │       ├── 0001_squashed_0004_remove_failuremode_effect_and_more.py
│   │       ├── 0002_auto_20220120_0839.py
│   │       ├── 0003_auto_20220203_1534.py
│   │       ├── 0004_remove_failuremode_effect_and_more.py
│   │       └── __init__.py
│   ├── katalogus/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── client.py
│   │   ├── exceptions.py
│   │   ├── forms/
│   │   │   ├── __init__.py
│   │   │   ├── katalogus_filter.py
│   │   │   └── plugin_settings.py
│   │   ├── health.py
│   │   ├── templates/
│   │   │   ├── about_plugins.html
│   │   │   ├── boefje_detail.html
│   │   │   ├── boefje_setup.html
│   │   │   ├── boefjes.html
│   │   │   ├── change_clearance_level.html
│   │   │   ├── clone_settings.html
│   │   │   ├── confirmation_clone_settings.html
│   │   │   ├── katalogus.html
│   │   │   ├── katalogus_settings.html
│   │   │   ├── normalizer_detail.html
│   │   │   ├── normalizers.html
│   │   │   ├── partials/
│   │   │   │   ├── boefje_tile.html
│   │   │   │   ├── enable_disable_plugin.html
│   │   │   │   ├── katalogus_filter.html
│   │   │   │   ├── katalogus_header.html
│   │   │   │   ├── katalogus_toolbar.html
│   │   │   │   ├── modal_report_types.html
│   │   │   │   ├── no_enabling_permission_message.html
│   │   │   │   ├── objects_to_scan.html
│   │   │   │   ├── plugin_settings_required.html
│   │   │   │   ├── plugin_tile.html
│   │   │   │   ├── plugin_tile_modal.html
│   │   │   │   ├── plugins.html
│   │   │   │   ├── plugins_navigation.html
│   │   │   │   ├── single_action_checkbox_form.html
│   │   │   │   └── single_action_form.html
│   │   │   ├── plugin_container_image.html
│   │   │   ├── plugin_settings_add.html
│   │   │   ├── plugin_settings_delete.html
│   │   │   └── plugin_settings_list.html
│   │   ├── urls.py
│   │   └── views/
│   │       ├── __init__.py
│   │       ├── boefje_setup.py
│   │       ├── change_clearance_level.py
│   │       ├── katalogus.py
│   │       ├── katalogus_settings.py
│   │       ├── mixins.py
│   │       ├── plugin_detail.py
│   │       ├── plugin_enable_disable.py
│   │       ├── plugin_settings_add.py
│   │       ├── plugin_settings_delete.py
│   │       ├── plugin_settings_edit.py
│   │       └── plugin_settings_list.py
│   ├── logging.json
│   ├── manage.py
│   ├── onboarding/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── forms.py
│   │   ├── migrations/
│   │   │   └── __init__.py
│   │   ├── templates/
│   │   │   ├── partials/
│   │   │   │   ├── cat_loader.html
│   │   │   │   ├── onboarding_header.html
│   │   │   │   ├── step_1_introduction_text.html
│   │   │   │   └── step_2_organization_text.html
│   │   │   ├── step_10_report.html
│   │   │   ├── step_1_introduction_registration.html
│   │   │   ├── step_1a_introduction.html
│   │   │   ├── step_2a_organization_setup.html
│   │   │   ├── step_2b_organization_update.html
│   │   │   ├── step_3_indemnification_setup.html
│   │   │   ├── step_4_trusted_acknowledge_clearance_level.html
│   │   │   ├── step_5_add_scan_ooi.html
│   │   │   ├── step_6_set_clearance_level.html
│   │   │   ├── step_7_clearance_level_introduction.html
│   │   │   ├── step_8_setup_scan_select_plugins.html
│   │   │   └── step_9_choose_report_type.html
│   │   ├── urls.py
│   │   ├── view_helpers.py
│   │   └── views.py
│   ├── package.json
│   ├── packaging/
│   │   ├── deb/
│   │   │   └── data/
│   │   │       ├── etc/
│   │   │       │   └── kat/
│   │   │       │       └── rocky.conf
│   │   │       └── usr/
│   │   │           └── bin/
│   │   │               └── rocky-cli
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   ├── reports/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── forms.py
│   │   ├── management/
│   │   │   ├── __init__.py
│   │   │   └── commands/
│   │   │       ├── __init__.py
│   │   │       └── worker.py
│   │   ├── migrations/
│   │   │   └── __init__.py
│   │   ├── report_types/
│   │   │   ├── __init__.py
│   │   │   ├── aggregate_organisation_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── appendix.html
│   │   │   │   ├── asset_overview.html
│   │   │   │   ├── basic_security.html
│   │   │   │   ├── findings.html
│   │   │   │   ├── recommendations.html
│   │   │   │   ├── report.html
│   │   │   │   ├── report.py
│   │   │   │   ├── rpki.html
│   │   │   │   ├── safe_connections.html
│   │   │   │   ├── summary.html
│   │   │   │   ├── system_specific.html
│   │   │   │   ├── system_specific_overview.html
│   │   │   │   ├── term_overview.html
│   │   │   │   └── vulnerabilities.html
│   │   │   ├── concatenated_report/
│   │   │   │   ├── __init__.py
│   │   │   │   └── report.py
│   │   │   ├── definitions.py
│   │   │   ├── dns_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── findings_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── helpers.py
│   │   │   ├── ipv6_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── mail_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── multi_organization_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── appendix.html
│   │   │   │   ├── asset_overview.html
│   │   │   │   ├── basic_security_details.html
│   │   │   │   ├── ipv6.html
│   │   │   │   ├── open_ports.html
│   │   │   │   ├── recommendations.html
│   │   │   │   ├── report.html
│   │   │   │   ├── report.py
│   │   │   │   ├── summary.html
│   │   │   │   └── vulnerabilities.html
│   │   │   ├── name_server_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── open_ports_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── rpki_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── safe_connections_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── systems_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── tls_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── vulnerability_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   └── web_system_report/
│   │   │       ├── __init__.py
│   │   │       ├── report.html
│   │   │       └── report.py
│   │   ├── runner/
│   │   │   ├── __init__.py
│   │   │   ├── models.py
│   │   │   ├── report_runner.py
│   │   │   └── worker.py
│   │   ├── serializers.py
│   │   ├── templates/
│   │   │   ├── aggregate_report.html
│   │   │   ├── aggregate_report_pdf.html
│   │   │   ├── forms/
│   │   │   │   └── report_form_fields.html
│   │   │   ├── generate_report/
│   │   │   │   ├── export_setup.html
│   │   │   │   ├── select_oois.html
│   │   │   │   ├── select_report_types.html
│   │   │   │   └── setup_scan.html
│   │   │   ├── generate_report.html
│   │   │   ├── generate_report_pdf.html
│   │   │   ├── multi_report.html
│   │   │   ├── multi_report_pdf.html
│   │   │   ├── partials/
│   │   │   │   ├── export_report_settings.html
│   │   │   │   ├── generate_report_header.html
│   │   │   │   ├── generate_report_sidemenu.html
│   │   │   │   ├── new_report_action_button.html
│   │   │   │   ├── plugin_overview_table.html
│   │   │   │   ├── report_findings_table.html
│   │   │   │   ├── report_header.html
│   │   │   │   ├── report_introduction.html
│   │   │   │   ├── report_names_form.html
│   │   │   │   ├── report_ooi_list.html
│   │   │   │   ├── report_setup_scan.html
│   │   │   │   ├── report_severity_totals_table.html
│   │   │   │   ├── report_sidemenu.html
│   │   │   │   ├── report_types_selection.html
│   │   │   │   ├── report_types_tiles.html
│   │   │   │   ├── report_types_tiles_required_optional.html
│   │   │   │   └── return_button.html
│   │   │   ├── report_overview/
│   │   │   │   ├── modal_partials/
│   │   │   │   │   ├── delete_modal.html
│   │   │   │   │   ├── enable_disable_schedule_modal.html
│   │   │   │   │   ├── rename_modal.html
│   │   │   │   │   ├── rerun_modal.html
│   │   │   │   │   └── share_modal.html
│   │   │   │   ├── report_history.html
│   │   │   │   ├── report_history_table.html
│   │   │   │   ├── report_overview_header.html
│   │   │   │   ├── report_overview_navigation.html
│   │   │   │   ├── scheduled_reports.html
│   │   │   │   ├── scheduled_reports_table.html
│   │   │   │   ├── subreports.html
│   │   │   │   ├── subreports_header.html
│   │   │   │   └── subreports_table.html
│   │   │   ├── report_schedules/
│   │   │   │   └── delete_recipe_modal.html
│   │   │   └── summary/
│   │   │       ├── ooi_selection.html
│   │   │       ├── report_asset_overview.html
│   │   │       ├── selected_plugins.html
│   │   │       └── service_health.html
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   └── report_extra.py
│   │   ├── urls.py
│   │   ├── utils.py
│   │   ├── views/
│   │   │   ├── __init__.py
│   │   │   ├── aggregate_report.py
│   │   │   ├── base.py
│   │   │   ├── generate_report.py
│   │   │   ├── mixins.py
│   │   │   ├── multi_report.py
│   │   │   ├── report_overview.py
│   │   │   └── view_helpers.py
│   │   └── viewsets.py
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── rocky/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── asgi.py
│   │   ├── auth/
│   │   │   ├── __init__.py
│   │   │   └── remote_user.py
│   │   ├── bytes_client.py
│   │   ├── exceptions.py
│   │   ├── forms.py
│   │   ├── health.py
│   │   ├── locale/
│   │   │   ├── de/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── django.pot
│   │   │   ├── en@pirate/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── es/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── fr/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── fy/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── it/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── nl/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── pap/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   └── ta/
│   │   │       └── LC_MESSAGES/
│   │   │           └── django.po
│   │   ├── messaging.py
│   │   ├── middleware/
│   │   │   ├── __init__.py
│   │   │   ├── auth_required.py
│   │   │   ├── onboarding.py
│   │   │   ├── otel.py
│   │   │   └── remote_user.py
│   │   ├── otel.py
│   │   ├── paginator.py
│   │   ├── permissions.py
│   │   ├── scheduler.py
│   │   ├── settings.py
│   │   ├── settings_test.py
│   │   ├── signals.py
│   │   ├── templates/
│   │   │   ├── 403.html
│   │   │   ├── 404.html
│   │   │   ├── admin/
│   │   │   │   ├── base.html
│   │   │   │   ├── change_form.html
│   │   │   │   ├── change_list.html
│   │   │   │   ├── delete_confirmation.html
│   │   │   │   ├── delete_selected_confirmation.html
│   │   │   │   ├── popup_response.html
│   │   │   │   └── prepopulated_fields_js.html
│   │   │   ├── dashboard_client.html
│   │   │   ├── dashboard_redteam.html
│   │   │   ├── finding_type_add.html
│   │   │   ├── findings/
│   │   │   │   ├── finding_add.html
│   │   │   │   ├── finding_list.html
│   │   │   │   └── findings_filter.html
│   │   │   ├── footer.html
│   │   │   ├── forms/
│   │   │   │   ├── json_schema_form.html
│   │   │   │   └── widgets/
│   │   │   │       ├── checkbox_group_boefje_tiles.html
│   │   │   │       ├── checkbox_group_columns.html
│   │   │   │       ├── checkbox_group_table.html
│   │   │   │       ├── checkbox_option.html
│   │   │   │       ├── datalist.html
│   │   │   │       ├── input_option.html
│   │   │   │       └── widget_label.html
│   │   │   ├── graph-d3.html
│   │   │   ├── head.html
│   │   │   ├── header.html
│   │   │   ├── health.html
│   │   │   ├── indemnification_add.html
│   │   │   ├── indemnification_present.html
│   │   │   ├── landing_page.html
│   │   │   ├── layouts/
│   │   │   │   └── base.html
│   │   │   ├── legal/
│   │   │   │   └── privacy_statement.html
│   │   │   ├── oois/
│   │   │   │   ├── error.html
│   │   │   │   ├── ooi_add.html
│   │   │   │   ├── ooi_add_type_select.html
│   │   │   │   ├── ooi_delete.html
│   │   │   │   ├── ooi_detail.html
│   │   │   │   ├── ooi_detail_add_related_object.html
│   │   │   │   ├── ooi_detail_findings_list.html
│   │   │   │   ├── ooi_detail_findings_overview.html
│   │   │   │   ├── ooi_detail_object.html
│   │   │   │   ├── ooi_detail_origins_declarations.html
│   │   │   │   ├── ooi_detail_origins_inference.html
│   │   │   │   ├── ooi_detail_origins_observations.html
│   │   │   │   ├── ooi_edit.html
│   │   │   │   ├── ooi_findings.html
│   │   │   │   ├── ooi_list.html
│   │   │   │   ├── ooi_mute_finding.html
│   │   │   │   ├── ooi_page_tabs.html
│   │   │   │   ├── ooi_past_due_warning.html
│   │   │   │   ├── ooi_summary.html
│   │   │   │   └── ooi_tree.html
│   │   │   ├── organizations/
│   │   │   │   ├── organization_add.html
│   │   │   │   ├── organization_edit.html
│   │   │   │   ├── organization_list.html
│   │   │   │   ├── organization_member_add.html
│   │   │   │   ├── organization_member_add_account_type.html
│   │   │   │   ├── organization_member_add_header.html
│   │   │   │   ├── organization_member_edit.html
│   │   │   │   ├── organization_member_list.html
│   │   │   │   ├── organization_member_upload.html
│   │   │   │   ├── organization_settings.html
│   │   │   │   ├── organization_tags.html
│   │   │   │   └── select_clearance_level_radio_input.html
│   │   │   ├── partials/
│   │   │   │   ├── boefje_tile_option.html
│   │   │   │   ├── current_config.html
│   │   │   │   ├── delete_ooi_modal.html
│   │   │   │   ├── edit_ooi_clearance_level_modal.html
│   │   │   │   ├── elements/
│   │   │   │   │   ├── definition_list_item.html
│   │   │   │   │   ├── definition_list_items.html
│   │   │   │   │   ├── ooi_add_type_select_form.html
│   │   │   │   │   ├── ooi_detail_settings.html
│   │   │   │   │   ├── ooi_list_settings_form.html
│   │   │   │   │   ├── ooi_report_settings.html
│   │   │   │   │   ├── ooi_tree_condensed_table.html
│   │   │   │   │   ├── ooi_tree_condensed_table_row.html
│   │   │   │   │   ├── ooi_tree_table.html
│   │   │   │   │   └── ooi_tree_table_row.html
│   │   │   │   ├── explanations.html
│   │   │   │   ├── finding_occurrence_definition_list.html
│   │   │   │   ├── findings_list_toolbar.html
│   │   │   │   ├── form/
│   │   │   │   │   ├── boefje_tiles_form.html
│   │   │   │   │   ├── checkbox_group_table_form.html
│   │   │   │   │   ├── field_hidden_from_list.html
│   │   │   │   │   ├── field_input.html
│   │   │   │   │   ├── field_input_checkbox.html
│   │   │   │   │   ├── field_input_errors.html
│   │   │   │   │   ├── field_input_help_text.html
│   │   │   │   │   ├── field_input_hidden.html
│   │   │   │   │   ├── field_input_multiselect.html
│   │   │   │   │   ├── field_input_radio.html
│   │   │   │   │   ├── field_input_wrapper.html
│   │   │   │   │   ├── fieldset.html
│   │   │   │   │   ├── form_errors.html
│   │   │   │   │   └── indemnification_add_form.html
│   │   │   │   ├── hyperlink_ooi_id.html
│   │   │   │   ├── hyperlink_ooi_type.html
│   │   │   │   ├── language-switcher.html
│   │   │   │   ├── list_filters.html
│   │   │   │   ├── list_paginator.html
│   │   │   │   ├── mute_findings_modal.html
│   │   │   │   ├── notifications_block.html
│   │   │   │   ├── ooi_detail_related_object.html
│   │   │   │   ├── ooi_detail_toolbar.html
│   │   │   │   ├── ooi_head.html
│   │   │   │   ├── ooi_list_filters.html
│   │   │   │   ├── ooi_list_toolbar.html
│   │   │   │   ├── ooi_report_findings_block.html
│   │   │   │   ├── ooi_report_findings_block_table.html
│   │   │   │   ├── ooi_report_findings_block_table_expanded_row.html
│   │   │   │   ├── ooi_schedule.html
│   │   │   │   ├── ooi_summary_block.html
│   │   │   │   ├── ooi_summary_finding.html
│   │   │   │   ├── ooi_tree_toolbar_bottom.html
│   │   │   │   ├── organization_member_list_filters.html
│   │   │   │   ├── organization_properties_table.html
│   │   │   │   ├── organizations_menu_dropdown.html
│   │   │   │   ├── page-meta.html
│   │   │   │   ├── pagination.html
│   │   │   │   ├── scan_level_indicator.html
│   │   │   │   ├── secondary-menu.html
│   │   │   │   ├── skip-to-content.html
│   │   │   │   └── stepper.html
│   │   │   ├── rest_framework/
│   │   │   │   └── api.html
│   │   │   ├── robots.txt
│   │   │   ├── scan.html
│   │   │   ├── scan_profiles/
│   │   │   │   └── scan_profile_detail.html
│   │   │   ├── tasks/
│   │   │   │   ├── boefje_task_detail.html
│   │   │   │   ├── boefjes.html
│   │   │   │   ├── normalizers.html
│   │   │   │   ├── ooi_detail_task_list.html
│   │   │   │   ├── partials/
│   │   │   │   │   ├── stats.html
│   │   │   │   │   ├── tab_navigation.html
│   │   │   │   │   ├── task_actions.html
│   │   │   │   │   ├── task_filter.html
│   │   │   │   │   └── tasks_overview_header.html
│   │   │   │   ├── plugin_detail_task_list.html
│   │   │   │   └── reports.html
│   │   │   ├── two_factor/
│   │   │   │   ├── _base.html
│   │   │   │   ├── _base_focus.html
│   │   │   │   ├── _wizard_actions.html
│   │   │   │   ├── _wizard_forms.html
│   │   │   │   ├── core/
│   │   │   │   │   ├── backup_tokens.html
│   │   │   │   │   ├── login.html
│   │   │   │   │   ├── otp_required.html
│   │   │   │   │   ├── phone_register.html
│   │   │   │   │   ├── setup.html
│   │   │   │   │   └── setup_complete.html
│   │   │   │   ├── profile/
│   │   │   │   │   ├── disable.html
│   │   │   │   │   └── profile.html
│   │   │   │   └── twilio/
│   │   │   │       └── sms_message.html
│   │   │   ├── upload_csv.html
│   │   │   └── upload_raw.html
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   └── rocky.py
│   │   ├── urls.py
│   │   ├── version.py
│   │   ├── views/
│   │   │   ├── __init__.py
│   │   │   ├── bytes_raw.py
│   │   │   ├── clients.py
│   │   │   ├── finding_add.py
│   │   │   ├── finding_list.py
│   │   │   ├── finding_type_add.py
│   │   │   ├── handler403.py
│   │   │   ├── handler404.py
│   │   │   ├── health.py
│   │   │   ├── indemnification_add.py
│   │   │   ├── landing_page.py
│   │   │   ├── mixins.py
│   │   │   ├── ooi_add.py
│   │   │   ├── ooi_delete.py
│   │   │   ├── ooi_detail.py
│   │   │   ├── ooi_detail_related_object.py
│   │   │   ├── ooi_edit.py
│   │   │   ├── ooi_findings.py
│   │   │   ├── ooi_list.py
│   │   │   ├── ooi_mute.py
│   │   │   ├── ooi_tree.py
│   │   │   ├── ooi_view.py
│   │   │   ├── organization_add.py
│   │   │   ├── organization_edit.py
│   │   │   ├── organization_list.py
│   │   │   ├── organization_member_add.py
│   │   │   ├── organization_member_edit.py
│   │   │   ├── organization_member_list.py
│   │   │   ├── organization_settings.py
│   │   │   ├── page_actions.py
│   │   │   ├── privacy_statement.py
│   │   │   ├── rerun_bits.py
│   │   │   ├── scan_profile.py
│   │   │   ├── scans.py
│   │   │   ├── scheduler.py
│   │   │   ├── task_detail.py
│   │   │   ├── tasks.py
│   │   │   ├── upload_csv.py
│   │   │   └── upload_raw.py
│   │   └── wsgi.py
│   ├── setup.py
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── account/
│   │   │   ├── __init__.py
│   │   │   └── test_login.py
│   │   ├── conftest.py
│   │   ├── integration/
│   │   │   ├── __init__.py
│   │   │   ├── conftest.py
│   │   │   ├── test_bench.py
│   │   │   ├── test_report_runner.py
│   │   │   └── test_reports.py
│   │   ├── katalogus/
│   │   │   ├── __init__.py
│   │   │   ├── test_katalogus.py
│   │   │   ├── test_katalogus_boefje_setup.py
│   │   │   ├── test_katalogus_plugin_add.py
│   │   │   ├── test_katalogus_plugin_delete.py
│   │   │   └── test_katalogus_plugin_detail.py
│   │   ├── objects/
│   │   │   ├── __init__.py
│   │   │   ├── test_objects.py
│   │   │   ├── test_objects_add.py
│   │   │   ├── test_objects_delete.py
│   │   │   ├── test_objects_detail.py
│   │   │   ├── test_objects_edit.py
│   │   │   ├── test_objects_findings.py
│   │   │   ├── test_objects_graph.py
│   │   │   ├── test_objects_scan_profile.py
│   │   │   └── test_objects_tree.py
│   │   ├── onboarding/
│   │   │   ├── __init__.py
│   │   │   └── test_onboarding.py
│   │   ├── reports/
│   │   │   ├── __init__.py
│   │   │   ├── test_aggregate_report_flow.py
│   │   │   ├── test_base_report.py
│   │   │   ├── test_dns_report.py
│   │   │   ├── test_findings_report.py
│   │   │   ├── test_generate_report_flow.py
│   │   │   ├── test_history_report.py
│   │   │   ├── test_ipv6_report.py
│   │   │   ├── test_mail_report.py
│   │   │   ├── test_multi_report_flow.py
│   │   │   ├── test_name_server_report.py
│   │   │   ├── test_open_ports_report.py
│   │   │   ├── test_plugins.py
│   │   │   ├── test_report_overview.py
│   │   │   ├── test_report_schedules.py
│   │   │   ├── test_report_tasks.py
│   │   │   ├── test_rpki_report.py
│   │   │   ├── test_safe_connections_report.py
│   │   │   ├── test_systems_report.py
│   │   │   ├── test_tls_report.py
│   │   │   ├── test_vulnerability_report.py
│   │   │   └── test_web_systems_report.py
│   │   ├── robot/
│   │   │   ├── ci/
│   │   │   │   └── 01_rocky_loads.robot
│   │   │   ├── complete_onboarding/
│   │   │   │   ├── 01_onboard_with_all_users.robot
│   │   │   │   ├── 02_login_as_redteamer_and_do_stuff.robot
│   │   │   │   └── 03_functional_tests.robot
│   │   │   ├── skip_onboarding_no_report/
│   │   │   │   └── 01_skip_onboarding_no_report.robot
│   │   │   ├── skip_onboarding_with_functional_tests/
│   │   │   │   ├── 01_skip_onboarding_with_functional_tests.robot
│   │   │   │   └── 02_functional_tests.robot
│   │   │   ├── skip_onboarding_with_report/
│   │   │   │   └── 01_skip_onboarding_with_report.robot
│   │   │   └── xxx.resource
│   │   ├── scheduler/
│   │   │   ├── __init__.py
│   │   │   └── test_scheduler_errors.py
│   │   ├── stubs/
│   │   │   ├── aggregate_report_data.json
│   │   │   ├── iana_service.html
│   │   │   ├── katalogus_boefjes.json
│   │   │   ├── katalogus_normalizers.json
│   │   │   ├── mock.csv
│   │   │   ├── multi_report_data_minvws.json
│   │   │   ├── multi_report_data_mispoes.json
│   │   │   ├── multi_report_post_processed_data.json
│   │   │   ├── question_schema.json
│   │   │   ├── snyk_response.html
│   │   │   └── wiki.html
│   │   ├── test_admin.py
│   │   ├── test_api.py
│   │   ├── test_api_organization.py
│   │   ├── test_boefjes_tasks.py
│   │   ├── test_core.py
│   │   ├── test_dashboard.py
│   │   ├── test_findings_add.py
│   │   ├── test_groups_and_permissions.py
│   │   ├── test_health.py
│   │   ├── test_indemnification.py
│   │   ├── test_landing_page.py
│   │   ├── test_members.py
│   │   ├── test_migrations.py
│   │   ├── test_models.py
│   │   ├── test_observed_at.py
│   │   ├── test_ooi_information.py
│   │   ├── test_organization.py
│   │   ├── test_privacy_statement.py
│   │   ├── test_scans.py
│   │   ├── test_upload_csv.py
│   │   ├── test_upload_members.py
│   │   ├── test_upload_raw.py
│   │   ├── test_validators.py
│   │   └── test_zip.py
│   ├── tools/
│   │   ├── __init__.py
│   │   ├── add_ooi_information.py
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── context_processors.py
│   │   ├── enums.py
│   │   ├── fields.py
│   │   ├── forms/
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── boefje.py
│   │   │   ├── finding_type.py
│   │   │   ├── findings.py
│   │   │   ├── ooi.py
│   │   │   ├── ooi_form.py
│   │   │   ├── scheduler.py
│   │   │   ├── settings.py
│   │   │   ├── upload_csv.py
│   │   │   ├── upload_oois.py
│   │   │   └── upload_raw.py
│   │   ├── management/
│   │   │   ├── __init__.py
│   │   │   └── commands/
│   │   │       ├── __init__.py
│   │   │       ├── almost_flush.py
│   │   │       ├── export_migrations.py
│   │   │       ├── makemessages.py
│   │   │       ├── setup_dev_account.py
│   │   │       └── setup_test_org.py
│   │   ├── migrations/
│   │   │   ├── 0001_initial.py
│   │   │   ├── 0001_squashed_0041_merge_20230731_1131.py
│   │   │   ├── 0002_alter_organization_octopoes_host.py
│   │   │   ├── 0003_change_orgazation_host_to_code.py
│   │   │   ├── 0004_ooiinformation.py
│   │   │   ├── 0005_scanprofile.py
│   │   │   ├── 0006_alter_organization_name.py
│   │   │   ├── 0007_update_job.py
│   │   │   ├── 0008_organizationmember_verified.py
│   │   │   ├── 0009_scanprofile_is_source_ooi.py
│   │   │   ├── 0010_alter_scanprofile_reference.py
│   │   │   ├── 0011_job_input_ooi.py
│   │   │   ├── 0012_rename_module_job_boefje_name.py
│   │   │   ├── 0013_boefjeconfig.py
│   │   │   ├── 0014_drop_dispatches_field.py
│   │   │   ├── 0015_alter_job_input_ooi.py
│   │   │   ├── 0016_organization_signal_fields.py
│   │   │   ├── 0017_alter_organizationmember_foreignkey.py
│   │   │   ├── 0018_alter_boefjeconfig_options.py
│   │   │   ├── 0019_alter_scanprofile_remove_level_and_user.py
│   │   │   ├── 0020_auto_20220524_1324.py
│   │   │   ├── 0021_delete_boefjeconfig.py
│   │   │   ├── 0022_alter_organization_options.py
│   │   │   ├── 0023_delete_scanprofile.py
│   │   │   ├── 0024_auto_20221005_1251.py
│   │   │   ├── 0025_auto_20221027_1233.py
│   │   │   ├── 0026_auto_20221031_1344.py
│   │   │   ├── 0027_auto_20230103_1721.py
│   │   │   ├── 0028_auto_20230117_1242.py
│   │   │   ├── 0029_alter_organizationtag_color.py
│   │   │   ├── 0029_set_user_full_name.py
│   │   │   ├── 0030_auto_20230227_1458.py
│   │   │   ├── 0031_merge_20230301_2012.py
│   │   │   ├── 0032_alter_organizationmember_user.py
│   │   │   ├── 0033_alter_organization_options.py
│   │   │   ├── 0033_auto_20230407_1113.py
│   │   │   ├── 0034_alter_organization_options.py
│   │   │   ├── 0034_organizationmember_groups.py
│   │   │   ├── 0035_update_ooi_delete_perm.py
│   │   │   ├── 0035_update_perms_move_and_clear_groups.py
│   │   │   ├── 0036_merge_20230504_1629.py
│   │   │   ├── 0037_alter_organization_options.py
│   │   │   ├── 0038_alter_organization_options.py
│   │   │   ├── 0038_delete_job.py
│   │   │   ├── 0039_merge_0038_alter_organization_options_0038_delete_job.py
│   │   │   ├── 0039_update_permissions.py
│   │   │   ├── 0040_admin_inherits_red_teamer_permissions.py
│   │   │   ├── 0040_update_admin_permission.py
│   │   │   ├── 0041_merge_20230731_1131.py
│   │   │   ├── 0042_alter_organization_options.py
│   │   │   ├── 0043_alter_organization_options.py
│   │   │   ├── 0044_alter_organization_options.py
│   │   │   ├── 0045_alter_organization_options.py
│   │   │   ├── 0045_update_permissions.py
│   │   │   ├── 0046_alter_organization_options.py
│   │   │   ├── 0047_alter_organization_code_alter_organization_name.py
│   │   │   └── __init__.py
│   │   ├── models.py
│   │   ├── ooi_helpers.py
│   │   ├── permissions.py
│   │   ├── serializers.py
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   ├── form_extra.py
│   │   │   ├── media_with_nonce.py
│   │   │   └── ooi_extra.py
│   │   ├── validators.py
│   │   ├── view_helpers.py
│   │   └── viewsets.py
│   └── whitelist.py
├── scripts/
│   ├── backup/
│   │   ├── backup-volumes.sh
│   │   └── restore-volumes.sh
│   ├── development/
│   │   └── backport.py
│   ├── installation/
│   │   ├── openkat-empty-job-queue.sh
│   │   ├── openkat-install.sh
│   │   ├── openkat-reset.sh
│   │   ├── openkat-restart.sh
│   │   ├── openkat-show-journal.sh
│   │   ├── openkat-start.sh
│   │   ├── openkat-status.sh
│   │   ├── openkat-stop.sh
│   │   └── openkat-update.sh
│   ├── migrate-openkat.sh
│   └── migrate.md
└── sonar-project.properties

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
*/.venv
*/.pytest_cache
*/__pycache__
*/.mypy_cache
*/.parcel-cache
*/.git
*/.github
*/node_modules
*/packaging
*/export_migrations
*/*.iml
*/Dockerfile
*/static


================================================
FILE: .env-defaults
================================================
# Container entrypoints will run database migrations if set to "true"
DATABASE_MIGRATION=true

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG=True

# SECURITY WARNING: enable two factor authentication in production!
TWOFACTOR_ENABLED=False

# --- Endpoints --- #
OCTOPOES_API=http://octopoes_api:80
SCHEDULER_API=http://scheduler:8000
KATALOGUS_API=http://katalogus:8000
XTDB_URI=http://crux:3000
BOEFJES_API=http://boefje:8000

# Bytes uses JWT for authentication
BYTES_API=http://bytes:8000

# CVE API - defaults to public instance, works out-of-the-box.
# For a local instance: COMPOSE_PROFILES=cveapi make kat
# and override with BOEFJE_CVEAPI_URL=http://cveapi:8080/v1 in .env
# See docs/source/installation-and-deployment/cveapi.rst
# The BOEFJE_ prefix is stripped by get_environment_settings() and passed
# to both local and containerized boefjes as CVEAPI_URL.
BOEFJE_CVEAPI_URL=https://cveapi.librekat.nl/v1

# Turn deduplication on by default
BOEFJES_DEDUPLICATE=true

# --- Rocky --- #
DJANGO_SUPERUSER_EMAIL=superuser@localhost
DJANGO_SUPERUSER_FULL_NAME="KAT Superuser"

# https://docs.openkat.nl/installation_and_deployment/hardening.html#django-allowed-hosts
DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost

# https://docs.openkat.nl/installation_and_deployment/hardening.html#django-csrf-trusted-origins
DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost,http://127.0.0.1

# This allows running pytest inside the container
ROCKY_DB_USER_CREATEDB=CREATEDB

# This is normally False when DEBUG is true, but we override that in settings.py
# so it possible to set DEBUG to True in production like environments.
COMPRESS_ENABLED=False


================================================
FILE: .env-dist
================================================
# === Notes === #

# {% ... } values are generated dynamically for security reasons when this file is copied through `make env`.
# After removing your `.env` file, you must run `make reset` to wipe the old container credentials.

# `.env` overrides variables from `.env-defaults` if included

# --- PostgreSQL --- #
POSTGRES_USER=postgres
POSTGRES_PASSWORD={%POSTGRES_PASSWORD}

# --- RabbitMQ --- #
RABBITMQ_DEFAULT_VHOST=kat
RABBITMQ_DEFAULT_USER={%QUEUE_USERNAME}
RABBITMQ_DEFAULT_PASS={%QUEUE_PASSWORD}

# --- OpenTelemetry --- #
# Uncomment to enable OpenTelemetry https://docs.openkat.nl/installation_and_deployment/localinstall.html#opentelemetry
# SPAN_EXPORT_GRPC_ENDPOINT=http://jaeger:4317

# --- Octopoes, Boefjes & Bytes shared --- #
QUEUE_URI=amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/${RABBITMQ_DEFAULT_VHOST}
BYTES_USERNAME={%BYTES_USERNAME}
BYTES_PASSWORD={%BYTES_PASSWORD}

### --- MODULE SPECIFIC SETTINGS --- ###

# --- Rocky --- #
# See `rocky/rocky/settings.py`

DJANGO_SUPERUSER_PASSWORD={%DJANGO_SUPERUSER_PASSWORD}

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY={%SECRET_KEY}

ROCKY_DB=rocky
ROCKY_DB_USER=rocky_app
ROCKY_DB_HOST=postgres
ROCKY_DB_PORT=5432
ROCKY_DB_PASSWORD={%ROCKY_DB_PASSWORD}


# --- Boefjes --- #
# See `boefjes/boefjes/config.py`

KATALOGUS_DB=katalogus
KATALOGUS_DB_USER=katalogus_app
KATALOGUS_DB_PASSWORD={%KATALOGUS_DB_PASSWORD}
KATALOGUS_DB_URI=postgresql://${KATALOGUS_DB_USER}:${KATALOGUS_DB_PASSWORD}@postgres:5432/${KATALOGUS_DB}

# --- Bytes --- #
# See `bytes/bytes/config.py`

BYTES_SECRET={%SECRET}

BYTES_DB=bytes
BYTES_DB_USER=bytes_app
BYTES_DB_PASSWORD={%BYTES_DB_PASSWORD}
BYTES_DB_URI=postgresql://${BYTES_DB_USER}:${BYTES_DB_PASSWORD}@postgres:5432/${BYTES_DB}


# --- Octopoes --- #
# See `octopoes/octopoes/config/settings.py`

# --- Mula --- #
# See `mula/scheduler/config/settings.py`

SCHEDULER_DB=scheduler
SCHEDULER_DB_USER=scheduler_app
SCHEDULER_DB_PASSWORD={%SCHEDULER_DB_PASSWORD}
SCHEDULER_DB_URI=postgresql://${SCHEDULER_DB_USER}:${SCHEDULER_DB_PASSWORD}@postgres:5432/${SCHEDULER_DB}


================================================
FILE: .env-prod
================================================
# `.env` overrides variables from `.env-prod` if included

# If you use docker-compose.release-example.yml as base for a docker compose
# based setup you can use this variable to define the version of OpenKAT you
# want to use. Example value: v1.9.0
# You can also use `latest` to use the most recently published images.
KAT_VERSION=latest

# Container entrypoints will run database migrations if set to "true"
DATABASE_MIGRATION=true

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG=False

# SECURITY WARNING: enable two factor authentication in production!
TWOFACTOR_ENABLED=True

# --- Endpoints --- #
OCTOPOES_API=http://octopoes_api:80
SCHEDULER_API=http://scheduler:8000
KATALOGUS_API=http://katalogus:8000
XTDB_URI=http://crux:3000
BOEFJES_API=http://boefje:8000

# Bytes uses JWT for authentication
BYTES_API=http://bytes:8000

# --- Rocky --- #
DJANGO_SUPERUSER_EMAIL=superuser@localhost
DJANGO_SUPERUSER_FULL_NAME="KAT Superuser"

# https://docs.openkat.nl/installation_and_deployment/hardening.html#django-allowed-hosts
DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost

# https://docs.openkat.nl/installation_and_deployment/hardening.html#django-csrf-trusted-origins
DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost,http://127.0.0.1


================================================
FILE: .gitattributes
================================================
# These settings are for any web project

# Handle line endings automatically for files detected as text
# and leave all files detected as binary untouched.
* text=auto

# Force the following filetypes to have unix eols, so Windows does not break them
*.* text eol=lf

# Windows forced line-endings
/.idea/* text eol=crlf

#
## These files are binary and should be left untouched
#

# (binary is a macro for -text -diff)
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary
*.ez binary
*.bz2 binary
*.swp binary


================================================
FILE: .github/CODEOWNERS
================================================
* @hasecon @cookiemonster @underdarknl


================================================
FILE: .github/ISSUE_TEMPLATE/.gitignore
================================================


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a bug report to help us improve
title: ""
labels: "bug"
assignees: ""
---

_Please add `bug`, the name of any relevant modules (e.g. `rocky`), and any other relevant labels to your issue._

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:

1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**OpenKAT version**
Note the release tag (and if possible: the installation method) here.
If it concerns an in-development version, note the branch(es) and commit hash(es) here as well.

**Desktop (please complete the following information if relevant):**

- OS: [e.g. iOS]
- Browser: [e.g. chrome, safari]
- Version: [e.g. 22]

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: KAT coordination repository
    url: https://github.com/minvws/nl-kat-coordination
  - name: KAT security issue reporting
    url: https://github.com/underdarknl
    about: If you find any serious security issues, please contact Jan Klopper (@underdarknl) directly and do NOT create a public issue.
  - name: KAT documentation
    url: https://minvws.github.io/nl-kat-coordination
    about: You can find all (developer) documentation on this webpage.
  - name: KAT security policy
    url: https://github.com/minvws/nl-kat-coordination/security/policy
  - name: KAT code of conduct
    url: https://github.com/minvws/.github/blob/master/CODE_OF_CONDUCT.md
  - name: KAT contributor agreement
    url: https://github.com/minvws/.github/blob/master/CONTRIBUTING.md


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ""
labels: "feature"
assignees: ""
---

_Please add one or more of the following labels to your issue:_
`frontend` `backend` `design` `documentation` `community` `dependencies`

## About this feature

### Detailed description

_Add detailed description of the new feature you'd like to propose. If this feature is related to a problem, what problem does it solve?_

### Feature benefit/User story

_Please also explain why this feature is important or necessary. What benefit does it bring to users? If possible, provide a user story format:_

As a [_type of user_], I want to [_action_] so that [_benefit_].

### Specifications

_Please add some specifications for the implementation. What needs to be implemented to match the design?_

The implementation should include…

- [ ] Specification 1
- [ ] Specification 2

### Additional information

_Any additional information, considerations, or context that might be helpful for understanding or evaluating the feature request._

## Design

_This part should only be filled in by the OpenKAT design team._

### Screenshots

_Include screenshots of the proposed design changes here._

### Figma link

_Link to the Figma design for further visualization (if applicable)_

## Implementation

_This part should only be filled in by the developers._

### Possible solution

_Outline your proposed solution for implementing the feature. You can include any specific ideas, designs, or functionalities here._

### Alternatives considered

_Describe any alternative approaches or solutions you've considered, and why you believe the proposed solution is superior._


================================================
FILE: .github/ISSUE_TEMPLATE/user_story.md
================================================
---
name: User story
about: Create a task that is phrased as a user story
title: ""
labels: ""
assignees: ""
---

## User Story

As a [type of user],
I want [some goal or desired outcome],
so that [some reason or benefit].

## Acceptance Criteria:

- [List specific conditions or criteria that must be met for the story to be considered complete.]


================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
  - package-ecosystem: "uv" # See documentation for possible values
    directory: "/" # Location of package manifests
    schedule:
      interval: "weekly"
    ignore:
      - dependency-name: "*"
        update-types: ["version-update:semver-minor"]
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"


================================================
FILE: .github/pull_request_template.md
================================================
### Changes

_Please describe the essence of this PR in a few sentences. Mention any breaking changes or required configuration steps._

### Issue link

_You have to create an issue to link to this PR. If this really is not possible, write a very detailed description here and add this PR to the project board directly._

_Please add the link to the issue after "Closes"._

Closes ...

### Demo

_Please add some proof in the form of screenshots or screen recordings to show (off) new functionality, if there are interesting new features for end-users._

### QA notes

_Please add some information for QA on how to test the newly created code._

---

### Code Checklist

<!--- Mandatory: --->

- [ ] All the commits in this PR are properly PGP-signed and verified.
- [ ] This PR only contains functionality relevant to the issue.
- [ ] I have written unit tests for the changes or fixes I made.
- [ ] I have checked the documentation and made changes where necessary.
- [ ] I have performed a self-review of my code and refactored it to the best of my abilities.

<!--- If applicable: --->

- [ ] Tickets have been created for newly discovered issues.
- [ ] For any non-trivial functionality, I have added integration and/or end-to-end tests.
- [ ] I have informed others of any required `.env` changes files if required and changed the `.env-dist` accordingly.
- [ ] I have included comments in the code to elaborate on what is not self-evident from the code itself, including references to issues and discussions online, or implicit behavior of an interface.

---

## Checklist for code reviewers:

Copy-paste the checklist from [the docs/source/developer-documentation/contributor/templates folder](https://github.com/minvws/nl-kat-coordination/blob/main/docs/source/developer-documentation/contributor/templates/pull_request_template_review_code.md) into your comment.

---

## Checklist for QA:

Copy-paste the checklist from [the docs/source/developer-documentation/contributor/templates folder](https://github.com/minvws/nl-kat-coordination/blob/main/docs/source/developer-documentation/contributor/templates/pull_request_template_review_qa.md) into your comment.


================================================
FILE: .github/scripts/commit_sign_push.sh
================================================
#!/bin/bash

#GITHUB_TOKEN should be ${{ secrets.GITHUB_TOKEN }}
#DESTINATION_BRANCH should be ${{ github.ref }}

FILES=$(git diff --name-only)
for FILE in $FILES; do
    SHA=$(git rev-parse "$DESTINATION_BRANCH":"$FILE")
    gh api --method PUT /repos/:owner/:repo/contents/"$FILE" \
        --field message="Update $FILE" \
        --field content=@<(base64 -i "$FILE") \
        --field branch="$DESTINATION_BRANCH" \
        --field sha="$SHA"
done


================================================
FILE: .github/scripts/coverage_file_fixer.py
================================================
#!/usr/bin/env python


# This script fixes the filename attribute in a coverage.xml file for SonarCloud coverage analysis
# These filenames should be relative to the base dir and not the coverage source directory


import argparse
import xml.etree.ElementTree as etree
from pathlib import Path


def path_prefixer(file: Path, prefix: Path) -> None:
    xml = file.read_text()
    root = etree.fromstring(xml)  # noqa: S314

    for element in root.findall(".//*[@filename]"):
        filename = element.get("filename")
        if filename is not None:
            element.set("filename", prefix.joinpath(filename).as_posix())

    file.write_text(etree.tostring(root).decode())


def main():
    parser = argparse.ArgumentParser(description="Prefix paths in coverage.xml files")
    parser.add_argument("file", type=Path, help="Path to the coverage.xml file.")
    parser.add_argument("prefix", type=Path, help="Path to prefix the filenames with.")
    args = parser.parse_args()

    path_prefixer(Path(args.file), Path(args.prefix))


if __name__ == "__main__":
    main()


================================================
FILE: .github/workflows/boefjes-tests.yml
================================================
name: Boefjes Test (with coverage)

on:
  workflow_call:

jobs:
  unit-tests:
    runs-on: ubuntu-24.04

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: "pip" # caching pip dependencies

      - name: Install pip
        run: python3 -m pip install --upgrade pip wheel

      - name: Install dev requirements
        run: grep -v git+https:// requirements-dev.txt | pip install -r /dev/stdin && grep git+https:// requirements-dev.txt | pip install -r /dev/stdin
        working-directory: boefjes/

      - name: Install plugin requirements
        run: find boefjes/plugins/ -name requirements.txt -execdir pip install -r requirements.txt \;
        working-directory: boefjes/

      - name: Install Octopoes
        run: cd octopoes && python setup.py bdist_wheel && pip install dist/octopoes*.whl

      - name: Run tests
        run: python3 -m pytest --cov boefjes/ --cov-report xml --cov-branch tests/
        working-directory: boefjes/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: boefjes-coverage-unit
          path: boefjes/.coverage
          include-hidden-files: true

  integration-tests:
    runs-on: ubuntu-24.04

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Run integration tests
        run: make itest
        working-directory: boefjes/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: boefjes-coverage-integration
          path: boefjes/.coverage
          include-hidden-files: true

  coverage:
    runs-on: ubuntu-24.04
    needs:
      - unit-tests
      - integration-tests

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: "pip" # caching pip dependencies

      - name: Install coverage
        run: pip install coverage[toml]

      - name: Download coverage artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: boefjes-coverage-*

      - name: Merge and generate coverage report
        run: |
          coverage combine ../**/.coverage
          coverage report
          coverage xml --ignore-errors
        working-directory: boefjes/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: boefjes-coverage
          path: boefjes/coverage.xml


================================================
FILE: .github/workflows/boefjes_container_image.yml
================================================
name: Boefjes Create container image

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - boefjes/**
      - octopoes/**
      - .github/workflows/boefjes_container_image.yml
  pull_request:
    paths:
      - boefjes/**
      - octopoes/**
      - .github/workflows/boefjes_container_image.yml

jobs:
  create_container_image:
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: |
            docker.underdark.nl/librekat/openkat-boefjes
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=ref,event=pr

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
        id: buildx

      - name: Login to Librekat Container Registry
        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          registry: docker.underdark.nl
          username: ${{ secrets.librekat_user }}
          password: ${{ secrets.librekat_password }}

      - name: Generate version.py
        run: |
          pip install setuptools-scm==7.1.0
          python -m setuptools_scm
          cp _version.py boefjes/boefjes/version.py
          cp _version.py boefjes/boefjes/katalogus/version.py

      - name: Build container image
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
        with:
          # We don't use git context because that doesn't process .dockerignore
          # https://github.com/docker/cli/issues/2827
          context: .
          file: boefjes/Dockerfile
          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max


================================================
FILE: .github/workflows/boefjes_tests.yml
================================================
name: Boefjes Run the test suite

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - boefjes/**
      - octopoes/**
      - .github/workflows/boefjes_tests.yml
  pull_request:
    paths:
      - boefjes/**
      - octopoes/**
      - .github/workflows/boefjes_tests.yml

jobs:
  Tests:
    permissions:
      contents: read
    strategy:
      fail-fast: false
      matrix:
        version: ["3.10", "3.11", "3.12", "3.13"]

    runs-on: ubuntu-24.04
    env:
      COMPOSE_FILE: .ci/docker-compose.yml

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: ${{ matrix.version }}
          cache: "pip" # caching pip dependencies

      - name: Install pip
        run: python3 -m pip install --upgrade pip

      - name: Install dev requirements
        run: grep -v git+https:// requirements-dev.txt | pip install -r /dev/stdin && grep git+https:// requirements-dev.txt | pip install -r /dev/stdin
        working-directory: ./boefjes

      - name: Install requirements
        run: find . -name requirements.txt | xargs -L 1 pip install -r
        working-directory: ./boefjes

      - name: Install Octopoes
        run: pip install wheel && cd octopoes && python setup.py bdist_wheel && pip install dist/octopoes*.whl

      - name: Run pytests
        run: python3 -m pytest
        working-directory: ./boefjes

      - name: Run integration tests
        run: make itest build_args='--build-arg PYTHON_VERSION=${{ matrix.version }}'
        working-directory: ./boefjes


================================================
FILE: .github/workflows/build-debian-docker-image.yml
================================================
name: Create and publish Docker image for building Debian packages

on:
  workflow_dispatch: {}
  push:
    branches:
      - "main"
    paths:
      - "packaging"
      - ".github/workflows/build-debian-docker-image.yml"
  pull_request:
    paths:
      - "packaging/**"
      - ".github/workflows/build-debian-docker-image.yml"

env:
  REGISTRY: docker.underdark.nl

jobs:
  build-debian-image:
    strategy:
      matrix:
        dist: [debian12, ubuntu22.04]
    runs-on: ubuntu-24.04
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Log in to the Container registry
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          registry: docker.underdark.nl
          username: ${{ secrets.librekat_user }}
          password: ${{ secrets.librekat_password }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: |
            docker.underdark.nl/librekat/openkat-${{ matrix.dist }}-build-image
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=ref,event=pr
            type=raw,value=latest,enable={{is_default_branch}}
            type=sha

      - name: Build and push Docker image
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
        with:
          context: ./packaging/${{ matrix.dist }}
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


================================================
FILE: .github/workflows/build-rdo-package.yml
================================================
name: Build RDO packages

concurrency:
  group: ${{ github.ref }}
  cancel-in-progress: true

on:
  push:
    tags:
      - v*
  workflow_dispatch:

env:
  PKGDIR: /home/runner/work/nl-kat-coordination

jobs:
  build:
    runs-on: ubuntu-22.04
    permissions:
      contents: write
      packages: write
    strategy:
      matrix:
        python_version: ["3.11"]

    # Generic bits
    steps:
      - name: Prep Set env
        run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV

      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: ${{ matrix.python_version }}
          cache: "pip"

      - name: Prep Install requirements
        run: sudo apt install gettext -y

      - name: Prep Replace version number with release version
        run: find . -type f -name 'version.py' -exec sed -ibak "s/__version__ = .*/__version__ = \"${RELEASE_VERSION}\"/" {} \;

      - name: Prep Configure git
        run: git config --global url."https://github.com/".insteadOf "ssh://git@github.com/"

      # Octopoes
      - name: Octopoes Create /var/lib/html
        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html

      - name: Octopoes copy project to run location.
        run: cp -rv * /var/www/html
        working-directory: ./octopoes

      - name: Octopoes Create env
        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv

      - name: Octopoes Install requirements
        run: cd /var/www/html; source .venv/bin/activate; pip install --upgrade pip; pip install --requirement requirements.txt

      - name: Octopoes Create venv archive
        run: tar -zcvf ${{ env.PKGDIR }}/octopoes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv

      - name: Octopoes Enable production logging config
        run: mv prod.logging.yml logging.yml
        working-directory: ./octopoes

      - name: Octopoes Create Octopoes release
        run: tar -cvzf ${{ env.PKGDIR }}/octopoes_${{ env.RELEASE_VERSION }}.tar.gz .
        working-directory: ./octopoes

      - name: Octopoes Build Octopoes wheel
        run: cd octopoes && /usr/bin/python3 setup.py bdist_wheel

      - name: Octopoes Cleanup
        run: sudo rm -rfv /var/www/html/

      # Rocky
      - name: Rocky Create /var/lib/html
        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html

      - name: Rocky copy project to run location.
        run: cp -rv * /var/www/html
        working-directory: ./rocky

      - name: Rocky Create env
        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv

      - name: Rocky Install requirements
        run: cd /var/www/html; source .venv/bin/activate; pip install --upgrade pip; grep -v git+https:// requirements.txt | pip install -r /dev/stdin ; grep git+https:// requirements.txt | pip install -r /dev/stdin; pip install --no-deps ${{ github.workspace }}/octopoes/dist/octopoes*.whl

      - name: Rocky Create rocky_venv tarball
        run: tar -zcvf ${{ env.PKGDIR }}/rocky_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv

      - name: Rocky Run nvm install
        run: nvm install v16
        shell: bash --login {0}
        working-directory: ./rocky

      - name: Rocky Run nvm use
        run: nvm use
        shell: bash --login {0}
        working-directory: ./rocky

      - name: Rocky Run yarn for rocky
        run: yarn
        shell: bash --login {0}
        working-directory: ./rocky

      - name: Rocky Run yarn build for rocky
        run: yarn build
        shell: bash --login {0}
        working-directory: ./rocky

      - name: Rocky Compilemessages
        run: /var/www/html/.venv/bin/python${{ matrix.python_version }} manage.py collectstatic && /var/www/html/.venv/bin/python${{ matrix.python_version }} manage.py compilemessages
        working-directory: ./rocky
        env:
          BYTES_API: http://bytes:8000
          BYTES_PASSWORD: password
          BYTES_USERNAME: username
          KATALOGUS_API: http://katalogus:8000
          OCTOPOES_API: http://octopoes_api:80
          SCHEDULER_API: http://scheduler:8000
          SECRET_KEY: whatever

      - name: Rocky Create rocky release
        run: tar -cvzf ${{ env.PKGDIR }}/rocky_${{ env.RELEASE_VERSION }}.tar.gz --exclude node_modules --exclude rocky_venv* --exclude=.git* --exclude .parcel-cache --exclude Dockerfile .
        working-directory: ./rocky

      # Bytes
      - name: Bytes Create /var/lib/html
        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html

      - name: Bytes copy project to run location.
        run: cp -rv * /var/www/html
        working-directory: ./bytes

      - name: Bytes Create env
        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv

      - name: Bytes Install requirements
        run: cd /var/www/html; source .venv/bin/activate; pip install --upgrade pip; pip install --requirement requirements.txt

      - name: Bytes Create bytes release
        run: tar -cvzf ${{ env.PKGDIR }}/bytes_${{ env.RELEASE_VERSION }}.tar.gz --exclude=./.git* .
        working-directory: ./bytes

      - name: Bytes Create bytes venv package
        run: tar -zcvf ${{ env.PKGDIR }}/bytes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv

      # Mula / Scheduler
      - name: Mula Create /var/lib/html
        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html

      - name: Mula copy project to run location.
        run: cp -rv * /var/www/html
        working-directory: ./mula

      - name: Mula Create env
        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv

      - name: Create scheduler release archive
        run: tar -cvzf ${{ env.PKGDIR }}/scheduler_${{ env.RELEASE_VERSION }}.tar.gz --exclude=./.git* --exclude=Makefile --exclude=Dockerfile --exclude=requirements* --exclude=tests .
        working-directory: ./mula

      - name: Create virtual env
        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv

      - name: Install requirements
        run: source .venv/bin/activate; pip install --upgrade pip; pip install --requirement requirements.txt
        working-directory: /var/www/html

      - name: Create venv archive
        run: tar -zcvf ${{ env.PKGDIR }}/scheduler_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv

      # Boefjes
      - name: Boefjes Create /var/lib/html
        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html

      - name: Boefjes copy project to run location.
        run: cp -rv * /var/www/html
        working-directory: ./boefjes

      - name: Boefjes Create env
        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv

      - name: Install requirements
        run: source .venv/bin/activate; pip install --upgrade pip; grep -v git+https:// requirements.txt | pip install -r /dev/stdin ; grep git+https:// requirements.txt | pip install -r /dev/stdin; pip install --no-deps ${{ github.workspace }}/octopoes/dist/octopoes*.whl
        working-directory: /var/www/html

      - name: Create archive
        run: tar -zcvf ${{ env.PKGDIR }}/boefjes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv

      - name: Create boefjes release
        run: tar -cvzf ${{ env.PKGDIR }}/boefjes_${{ env.RELEASE_VERSION }}.tar.gz .
        working-directory: ./boefjes

      # Common / Uploads
      - name: Octopoes Upload whl package
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: "octopoes-${{env.RELEASE_VERSION}}_python${{ matrix.python_version }}"
          path: "${{ github.workspace }}/octopoes/dist/octopoes*.whl"

      - name: Octopoes Upload venv tar
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: octopoes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}
          path: ${{ env.PKGDIR }}/octopoes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz

      - name: Octopoes Upload octopoes tar
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: octopoes_${{ env.RELEASE_VERSION }}
          path: ${{ env.PKGDIR }}/octopoes_${{ env.RELEASE_VERSION }}.tar.gz

      - name: Rocky Upload rocky tar
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: rocky_${{ env.RELEASE_VERSION }}
          path: ${{ env.PKGDIR }}/rocky_${{ env.RELEASE_VERSION }}.tar.gz

      - name: Rocky Upload venv tar
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: rocky_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}
          path: ${{ env.PKGDIR }}/rocky_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz

      - name: Bytes Upload bytes tar
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: bytes_${{ env.RELEASE_VERSION }}
          path: ${{ env.PKGDIR }}/bytes_${{ env.RELEASE_VERSION }}.tar.gz

      - name: Bytes Upload bytes venv tar
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: bytes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}
          path: ${{ env.PKGDIR }}/bytes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz

      - name: Upload scheduler release
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: scheduler_${{ env.RELEASE_VERSION }}
          path: ${{ env.PKGDIR }}/scheduler_${{ env.RELEASE_VERSION }}.tar.gz

      - name: Upload venv archive
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: scheduler_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}
          path: ${{ env.PKGDIR }}/scheduler_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz

      - name: Upload boefjes tar
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: boefjes_${{ env.RELEASE_VERSION }}
          path: ${{ env.PKGDIR }}/boefjes_${{ env.RELEASE_VERSION }}.tar.gz

      - name: Upload venv tar
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: boefjes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}
          path: ${{ env.PKGDIR }}/boefjes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz


================================================
FILE: .github/workflows/build_docs_on_pr.yml
================================================
name: Build docs artifact for PR

on:
  pull_request:
    paths:
      # We generate documentation for boefje, bytes, mula and octopoes
      # from code so the workflow should also depend on it.
      - "boefje/**"
      - "bytes/**"
      - "docs/**"
      - "mula/**"
      - "octopoes/**"
      - "requirements.txt"
      - ".github/workflows/build_docs_on_pr.yml"

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  build-artifact:
    permissions:
      contents: read
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: 3.13
          cache: "pip" # caching pip dependencies

      - name: Install pip dependencies
        run: pip install -r requirements.txt

      - name: Compile static HTML
        run: make docs

      - name: Upload artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: "github-pages-no-deploy"
          path: "docs/_build"


================================================
FILE: .github/workflows/bytes-tests.yml
================================================
name: Bytes Tests (with coverage)

on:
  workflow_call:

jobs:
  unit-tests:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Run unit tests
        run: make utest
        working-directory: bytes/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: bytes-coverage-unit
          path: bytes/.coverage
          include-hidden-files: true

  integration-tests:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Run integration tests
        run: make itest
        working-directory: bytes/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: bytes-coverage-integration
          path: bytes/.coverage
          include-hidden-files: true

  coverage:
    runs-on: ubuntu-24.04
    needs:
      - unit-tests
      - integration-tests

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: "pip" # caching pip dependencies

      - name: Install coverage
        run: pip install coverage[toml]

      - name: Download coverage artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: bytes-coverage-*

      - name: Merge and generate coverage report
        run: |
          coverage combine ../**/.coverage
          coverage report
          coverage xml --ignore-errors
        working-directory: bytes/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: bytes-coverage
          path: bytes/coverage.xml


================================================
FILE: .github/workflows/bytes_container_image.yml
================================================
name: Bytes Create container image

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - bytes/**
      - .github/workflows/bytes_container_image.yml
  pull_request:
    paths:
      - bytes/**
      - .github/workflows/bytes_container_image.yml

jobs:
  create_container_image:
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: |
            docker.underdark.nl/librekat/openkat-bytes
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=ref,event=pr

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
        id: buildx

      - name: Login to librekat Container Registry
        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          registry: docker.underdark.nl
          username: ${{ secrets.librekat_user }}
          password: ${{ secrets.librekat_password }}

      - name: Generate version.py
        run: |
          pip install setuptools-scm==7.1.0
          python -m setuptools_scm
          cp _version.py bytes/bytes/version.py

      - name: Build container image
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
        with:
          # We don't use git context because that doesn't process .dockerignore
          # https://github.com/docker/cli/issues/2827
          context: bytes
          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max


================================================
FILE: .github/workflows/bytes_tests.yml
================================================
name: Bytes Run the test suite

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - bytes/**
      - .github/workflows/bytes_tests.yml
  pull_request:
    paths:
      - bytes/**
      - .github/workflows/bytes_tests.yml

jobs:
  test:
    permissions:
      contents: read
    strategy:
      fail-fast: false
      matrix:
        version: ["3.10", "3.11", "3.12", "3.13"]

    runs-on: ubuntu-24.04

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Run unit tests
        run: make utest build_args='--build-arg PYTHON_VERSION=${{ matrix.version }}'
        working-directory: ./bytes

      - name: Run integration tests
        run: SLEEP_TIME=5 make itest build_args='--build-arg PYTHON_VERSION=${{ matrix.version }}'
        working-directory: ./bytes


================================================
FILE: .github/workflows/check_requirements.yml
================================================
name: Check dependencies

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - "**/requirements.txt"
      - "**/requirements-dev.txt"
      - "**/pyproject.toml"
      - "**/uv.lock"
      - ".github/workflows/check_requirements.yml"
  pull_request:
    paths:
      - "**/requirements.txt"
      - "**/requirements-dev.txt"
      - "**/pyproject.toml"
      - "**/uv.lock"
      - ".github/workflows/check_requirements.yml"

jobs:
  poetry-dependencies:
    permissions:
      contents: read

    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        id: py313
        with:
          python-version: 3.13
          cache: pip

      - name: Install uv
        run: pip install uv==0.7.13

      - name: Check, lock, and export requirements
        run: make requirements

      - name: Check if there are any changed files
        if: ${{ github.actor != 'dependabot[bot]' }}
        run: git diff --exit-code


================================================
FILE: .github/workflows/codeql.yml
================================================
name: "CodeQL OpenKAT"

on:
  push:
    branches:
      - "main"
      - "release-*"
  pull_request:
  schedule:
    # Weekly on Sunday.
    - cron: "30 1 * * 0"

jobs:
  analyze:
    name: Analyze (${{ matrix.language }})
    runs-on: ["ubuntu-latest"]
    permissions:
      # required for all workflows
      security-events: write
    strategy:
      fail-fast: false
      matrix:
        include:
          - language: javascript-typescript
            build-mode: none
          - language: python
            build-mode: none
          - language: actions
            build-mode: none

    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      # Initializes the CodeQL tools for scanning.
      - name: Initialize CodeQL
        uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
        with:
          languages: ${{ matrix.language }}
          build-mode: ${{ matrix.build-mode }}
          queries: security-extended # Use security extended, when too many false positives we can switch back to default

      # Use the CodeQL tools for analyzing.
      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
        with:
          category: "/language:${{matrix.language}}"


================================================
FILE: .github/workflows/containerized_boefjes.yml
================================================
name: Build containerized boefjes

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - boefjes/boefjes/plugins/**
      - boefjes/images/**
      - .github/workflows/containerized_boefjes.yml
  pull_request:
    paths:
      - boefjes/boefjes/plugins/kat_nmap_tcp/**
      - boefjes/boefjes/plugins/kat_nmap_udp/**
      - boefjes/boefjes/plugins/kat_dnssec/**
      - boefjes/boefjes/plugins/kat_nikto/**
      - boefjes/boefjes/plugins/kat_export_http/**
      - boefjes/images/**
      - .github/workflows/containerized_boefjes.yml

jobs:
  build_containerized_boefjes:
    permissions:
      contents: read
      packages: write
    strategy:
      fail-fast: false
      matrix:
        include:
          - dockerfile: boefjes/boefjes/plugins/kat_nmap_tcp/boefje.Dockerfile
            image: openkat-nmap
          - dockerfile: boefjes/boefjes/plugins/kat_dnssec/boefje.Dockerfile
            image: openkat-dns-sec
          - dockerfile: boefjes/boefjes/plugins/kat_export_http/boefje.Dockerfile
            image: openkat-export-http
          - dockerfile: boefjes/boefjes/plugins/kat_nikto/boefje.Dockerfile
            image: openkat-nikto
          - dockerfile: boefjes/images/generic.Dockerfile
            image: openkat-generic
          - dockerfile: boefjes/boefjes/plugins/kat_adr_validator/boefje.Dockerfile
            image: openkat-adr-validator
          - dockerfile: boefjes/boefjes/plugins/kat_masscan/boefje.Dockerfile
            image: openkat-masscan
          - dockerfile: boefjes/boefjes/plugins/kat_nuclei_cve/boefje.Dockerfile
            image: openkat-nuclei
          - dockerfile: boefjes/boefjes/plugins/kat_ssl_certificates/boefje.Dockerfile
            image: openkat-ssl-certificates
          - dockerfile: boefjes/boefjes/plugins/kat_ssl_scan/boefje.Dockerfile
            image: openkat-ssl-scan
          - dockerfile: boefjes/boefjes/plugins/kat_testssl_sh_ciphers/boefje.Dockerfile
            image: openkat-testssl-sh-ciphers
          - dockerfile: boefjes/boefjes/plugins/kat_webpage_capture/boefje.Dockerfile
            image: openkat-webpage-capture
          - dockerfile: boefjes/boefjes/plugins/kat_wpscan/boefje.Dockerfile
            image: openkat-wp-scan
          - dockerfile: boefjes/boefjes/plugins/kat_pdio_subfinder/boefje.Dockerfile
            image: openkat-pdio-subfinder
    runs-on: ubuntu-24.04
    services:
      registry:
        image: registry:2
        ports:
          - 5000:5000
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
        id: buildx
        with:
          driver-opts: network=host

      - name: Build the boefje base image for ${{ matrix.image }}
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
        with:
          context: ./boefjes
          file: boefjes/images/base.Dockerfile
          # We don't push as this is an intermediate image that handles common operations for most boefjes
          load: true
          push: true
          tags: localhost:5000/openkat/boefje-base:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: |
            docker.underdark.nl/librekat/${{ matrix.image }}
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=ref,event=pr

      - name: Login to librekat Container Registry
        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          registry: docker.underdark.nl
          username: ${{ secrets.librekat_user }}
          password: ${{ secrets.librekat_password }}

      - name: Build container image for ${{ matrix.image }}
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
        with:
          # We don't use git context because that doesn't process .dockerignore
          # https://github.com/docker/cli/issues/2827
          context: ./boefjes
          file: ${{ matrix.dockerfile }}
          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-contexts: |
            openkat/boefje-base:latest=docker-image://localhost:5000/openkat/boefje-base:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max


================================================
FILE: .github/workflows/debian_package.yml
================================================
name: Debian packages

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
  pull_request:

jobs:
  changes:
    permissions: {}
    runs-on: ubuntu-24.04
    outputs:
      packages: ${{ steps.filter.outputs.changes }}
    steps:
      - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
        if: github.event_name != 'push'
        id: filter
        with:
          filters: |
            boefjes:
              - 'boefjes/**'
              - 'octopoes/**'
              - '.github/workflows/debian_package.yml'
            bytes:
              - 'bytes/**'
              - '.github/workflows/debian_package.yml'
            cveapi:
              - 'cveapi/**'
              - '.github/workflows/debian_package.yml'
            mula:
              - 'mula/**'
              - '.github/workflows/debian_package.yml'
            octopoes:
              - 'octopoes/**'
              - '.github/workflows/debian_package.yml'
            rocky:
              - 'rocky/**'
              - 'octopoes/**'
              - '.github/workflows/debian_package.yml'

  build:
    permissions:
      contents: read
    needs: changes
    if: ${{ github.event_name == 'push' || (needs.changes.outputs.packages != '[]' && needs.changes.outputs.packages != '') }}
    strategy:
      fail-fast: false
      matrix:
        dist: [debian12, ubuntu22.04]
        # On main, release branches and tags we always want to build all the packages
        package: ${{ github.event_name == 'push' && fromJSON('["boefjes", "bytes", "cveapi", "mula", "octopoes", "rocky"]') || fromJSON(needs.changes.outputs.packages) }}
        exclude:
          - package: cveapi
            dist: ubuntu22.04
    runs-on: ubuntu-24.04
    env:
      PKG_NAME: kat-${{ matrix.package }}

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Generate version.py and set RELEASE_VERSION
        run: |
          pip install setuptools-scm==7.1.0
          echo "RELEASE_VERSION=$(python -m setuptools_scm | sed s/rc/~rc/)" >> $GITHUB_ENV
          cp _version.py boefjes/boefjes/version.py
          cp _version.py boefjes/boefjes/katalogus/version.py
          cp _version.py bytes/bytes/version.py
          cp _version.py mula/scheduler/version.py
          cp _version.py octopoes/octopoes/version.py
          cp _version.py rocky/rocky/version.py

      - name: Run debian package build
        uses: maus007/docker-run-action-fork@5ddaad0f7eedd03f64e412b1931852bd3031b273
        with:
          run: packaging/scripts/build-debian-package.sh
          registry: docker.underdark.nl
          image: docker.underdark.nl/librekat/openkat-${{ matrix.dist }}-build-image:latest
          username: ${{ secrets.librekat_user }}
          password: ${{ secrets.librekat_password }}
          options: -v ${{ github.workspace }}/${{ matrix.package }}:/app
            -v ${{ github.workspace }}/octopoes:/octopoes
            -e REPOSITORY=${{ github.repository }}
            -e RELEASE_VERSION=${{ env.RELEASE_VERSION }}
            -e RELEASE_TAG=${{ env.RELEASE_TAG }}
            -e PKG_NAME=${{ env.PKG_NAME }}
            --workdir /app

      - name: Upload .deb to artifacts
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: ${{env.PKG_NAME}}_${{ env.RELEASE_VERSION }}_${{ matrix.dist }}.deb
          path: ${{matrix.package}}/build/${{env.PKG_NAME}}_${{ env.RELEASE_VERSION }}_${{ matrix.package == 'cveapi' && 'all' || 'amd64' }}.deb

  add-debian-packages-to-release:
    permissions:
      contents: write
    needs: build
    runs-on: ubuntu-24.04
    if: startsWith(github.ref, 'refs/tags/')

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Generate version.py and set RELEASE_VERSION
        run: |
          pip install setuptools-scm==7.1.0
          echo "RELEASE_VERSION=$(python -m setuptools_scm | sed s/rc/~rc/)" >> $GITHUB_ENV

      - name: Download all assets for debian12
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: kat-debian12-${{ env.RELEASE_VERSION }}
          merge-multiple: true
          pattern: kat-*_debian12.deb

      - name: Download all assets for ubuntu22.04
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: kat-ubuntu22.04-${{ env.RELEASE_VERSION }}
          merge-multiple: true
          pattern: kat-*_ubuntu22.04.deb

      - name: Move cve-api asset to add it as a separate artifact
        run: mv kat-debian12-${{ env.RELEASE_VERSION }}/kat-cveapi_${{ env.RELEASE_VERSION }}_all.deb .

      - name: Bundle assets
        run: |
          tar -cvzf kat-debian12-${{ env.RELEASE_VERSION }}.tar.gz -C kat-debian12-${{ env.RELEASE_VERSION }} .
          tar -cvzf kat-ubuntu22.04-${{ env.RELEASE_VERSION }}.tar.gz -C kat-ubuntu22.04-${{ env.RELEASE_VERSION }} .

      - name: Add Release Assets
        uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
        with:
          generate_release_notes: true
          files: |
            kat-debian12-${{ env.RELEASE_VERSION }}.tar.gz
            kat-ubuntu22.04-${{ env.RELEASE_VERSION }}.tar.gz
            kat-cveapi_${{ env.RELEASE_VERSION }}_all.deb


================================================
FILE: .github/workflows/deploy_docs.yml
================================================
name: Compile and deploy documentation to Pages

on:
  push:
    branches:
      - "main"

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow one concurrent deployment
concurrency:
  group: "pages"
  cancel-in-progress: true

jobs:
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: 3.13
          cache: "pip" # caching pip dependencies

      - name: Install pip dependencies
        run: pip install -r requirements.txt

      - name: Compile static HTML
        run: make docs

      - name: Setup Pages
        uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0

      - name: Upload artifact
        uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
        with:
          path: "docs/_build"

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0


================================================
FILE: .github/workflows/mula-tests.yml
================================================
name: Mula Tests (with coverage)

on:
  workflow_call:

jobs:
  unit-tests:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Run unit tests
        run: make utest
        working-directory: mula/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: mula-coverage-unit
          path: mula/.coverage
          include-hidden-files: true

  integration-tests:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Run integration tests
        run: make itest
        working-directory: mula/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: mula-coverage-integration
          path: mula/.coverage
          include-hidden-files: true

  coverage:
    runs-on: ubuntu-24.04
    needs:
      - unit-tests
      - integration-tests

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: "pip" # caching pip dependencies

      - name: Install coverage
        run: pip install coverage[toml]

      - name: Download coverage artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: mula-coverage-*

      - name: Merge and generate coverage report
        run: |
          coverage combine ../**/.coverage
          coverage report
          coverage xml --ignore-errors
        working-directory: mula/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: mula-coverage
          path: mula/coverage.xml


================================================
FILE: .github/workflows/mula_container_image.yml
================================================
name: Mula Create container image

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - mula/**
      - .github/workflows/mula_container_image.yml
  pull_request:
    paths:
      - mula/**
      - .github/workflows/mula_container_image.yml

jobs:
  create_container_image:
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: |
            docker.underdark.nl/librekat/openkat-mula
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=ref,event=pr

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
        id: buildx

      - name: Login to the Container Registry
        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          registry: docker.underdark.nl
          username: ${{ secrets.librekat_user }}
          password: ${{ secrets.librekat_password }}

      - name: Generate version.py
        run: |
          pip install setuptools-scm==7.1.0
          python -m setuptools_scm
          cp _version.py mula/scheduler/version.py

      - name: Build container image
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
        with:
          # We don't use git context because that doesn't process .dockerignore
          # https://github.com/docker/cli/issues/2827
          context: mula
          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max


================================================
FILE: .github/workflows/mula_tests.yml
================================================
name: Mula Run the test suite

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - mula/**
      - .github/workflows/mula_tests.yml
  pull_request:
    paths:
      - mula/**
      - .github/workflows/mula_tests.yml

jobs:
  test:
    permissions:
      contents: read
    strategy:
      fail-fast: false
      matrix:
        version: ["3.10", "3.11", "3.12", "3.13"]

    runs-on: ubuntu-24.04
    env:
      COMPOSE_FILE: .ci/docker-compose.yml

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Build the images
        run: DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose --project-directory . -f .ci/docker-compose.yml build --build-arg PYTHON_VERSION=${{ matrix.version }}
        working-directory: ./mula

      - name: Run unit tests
        run: make utest
        working-directory: ./mula

      - name: Run integration tests
        run: make itest
        working-directory: ./mula


================================================
FILE: .github/workflows/octopoes-tests.yml
================================================
name: Octopoes Tests (with coverage)

on:
  workflow_call:

jobs:
  unit-tests:
    runs-on: ubuntu-24.04

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: "pip" # caching pip dependencies

      - name: Install requirements
        run: pip install -r requirements-dev.txt
        working-directory: octopoes/

      - name: Run unit tests
        run: pytest --cov octopoes/ tests/
        working-directory: octopoes/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: octopoes-coverage-unit
          path: octopoes/.coverage
          include-hidden-files: true

  integration-tests:
    runs-on: ubuntu-24.04

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Run integration tests
        run: make itest
        working-directory: octopoes/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: octopoes-coverage-integration
          path: octopoes/.coverage
          include-hidden-files: true

  coverage:
    runs-on: ubuntu-24.04
    needs:
      - unit-tests
      - integration-tests

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: "pip" # caching pip dependencies

      - name: Install coverage
        run: pip install coverage[toml]

      - name: Download coverage artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: octopoes-coverage-*

      - name: Merge and generate coverage report
        run: |
          coverage combine ../**/.coverage
          coverage report
          coverage xml --ignore-errors
        working-directory: octopoes/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: octopoes-coverage
          path: octopoes/coverage.xml


================================================
FILE: .github/workflows/octopoes_container_image.yml
================================================
name: Octopoes Create container image

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - octopoes/**
      - .github/workflows/octopoes_container_image.yml
  pull_request:
    paths:
      - octopoes/**
      - .github/workflows/octopoes_container_image.yml

jobs:
  create_container_image:
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: |
            docker.underdark.nl/librekat/openkat-octopoes
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=ref,event=pr

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
        id: buildx

      - name: Login to GitHub Container Registry
        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          registry: docker.underdark.nl
          username: ${{ secrets.librekat_user }}
          password: ${{ secrets.librekat_password }}

      - name: Generate version.py
        run: |
          pip install setuptools-scm==7.1.0
          python -m setuptools_scm
          cp _version.py octopoes/octopoes/version.py

      - name: Build container image
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
        with:
          # We don't use git context because that doesn't process .dockerignore
          # https://github.com/docker/cli/issues/2827
          context: octopoes
          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max


================================================
FILE: .github/workflows/octopoes_rtest.yml
================================================
name: Octopoes Run the robot framework integration tests

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - octopoes/**
      - .github/workflows/octopoes_rtest.yml
  pull_request:
    paths:
      - octopoes/**
      - .github/workflows/octopoes_rtest.yml

jobs:
  rtest:
    permissions:
      contents: read
    runs-on: ubuntu-24.04

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: "pip" # caching pip dependencies

      - name: Install requirements-dev.txt
        run: pip install -r requirements-dev.txt
        working-directory: ./octopoes

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
        id: buildx

      - name: Run robot tests
        run: make rtest
        env:
          DOCKER_BUILDKIT: 1
        working-directory: ./octopoes


================================================
FILE: .github/workflows/octopoes_tests.yml
================================================
name: Octopoes tests

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - octopoes/**
      - .github/workflows/octopoes_tests.yml
  pull_request:
    paths:
      - octopoes/**
      - .github/workflows/octopoes_tests.yml

jobs:
  test:
    permissions:
      contents: read
    strategy:
      fail-fast: false
      matrix:
        version: ["3.10", "3.11", "3.12", "3.13"]

    runs-on: ubuntu-24.04

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Python ${{ matrix.version }}
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: ${{ matrix.version }}
          cache: "pip" # caching pip dependencies

      - name: Install requirements.txt
        run: pip install -r requirements-dev.txt
        working-directory: ./octopoes

      - name: Run unit tests
        run: pytest
        working-directory: ./octopoes

      - name: Run integration tests
        run: DOCKER_BUILDKIT=1 make itest build_args='--build-arg PYTHON_VERSION=${{ matrix.version }}'
        working-directory: ./octopoes


================================================
FILE: .github/workflows/pre_commit_checks.yml
================================================
name: Pre-commit checks

on:
  workflow_call:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
  pull_request:

jobs:
  pre-commit:
    permissions:
      contents: read
    runs-on: ubuntu-24.04

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        id: setup-python
        with:
          python-version: 3.13
          cache: pip

      - name: Install prek
        run: pip install prek==0.3.2

      - name: Cache prek
        uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
        with:
          path: ~/.cache/prek
          key: prek|${{ steps.setup-python.outputs.python-version }}|${{ hashFiles('.pre-commit-config.yaml') }}

      - name: Run prek
        run: prek run --all-files --show-diff-on-failure --color always


================================================
FILE: .github/workflows/rocky-tests.yml
================================================
name: Rocky Tests (with coverage)

on:
  workflow_call:

jobs:
  unit-tests:
    runs-on: ubuntu-24.04

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Run unit tests
        run: make utest
        working-directory: rocky/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: rocky-coverage-unit
          path: rocky/.coverage
          include-hidden-files: true

  integration-tests:
    runs-on: ubuntu-24.04

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Run integration tests
        run: make itest
        working-directory: rocky/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: rocky-coverage-integration
          path: rocky/.coverage
          include-hidden-files: true

  coverage:
    runs-on: ubuntu-24.04
    needs:
      - unit-tests
      - integration-tests

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: "pip" # caching pip dependencies

      - name: Install coverage
        run: pip install coverage[toml]

      - name: Download coverage artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: rocky-coverage-*

      - name: Merge and generate coverage report
        run: |
          coverage combine ../**/.coverage
          coverage report
          coverage xml --ignore-errors
        working-directory: rocky/

      - name: Upload coverage as artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: rocky-coverage
          path: rocky/coverage.xml


================================================
FILE: .github/workflows/rocky_container_image.yml
================================================
name: Rocky Create container image

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - octopoes/**
      - rocky/**
      - .github/workflows/rocky_container_image.yml
  pull_request:
    paths:
      - octopoes/**
      - rocky/**
      - .github/workflows/rocky_container_image.yml

jobs:
  create_container_image:
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: |
            docker.underdark.nl/librekat/openkat-rocky
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=ref,event=pr

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
        id: buildx

      - name: Login to the Container Registry
        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          registry: docker.underdark.nl
          username: ${{ secrets.librekat_user }}
          password: ${{ secrets.librekat_password }}

      - name: Generate version.py
        run: |
          pip install setuptools-scm==7.1.0
          python -m setuptools_scm
          cp _version.py rocky/rocky/version.py

      - name: Build container image
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
        with:
          # We don't use git context because that doesn't process .dockerignore
          # https://github.com/docker/cli/issues/2827
          context: .
          file: rocky/Dockerfile
          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max


================================================
FILE: .github/workflows/rocky_makelang.yml
================================================
name: Rocky Check if translations are up to date

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - rocky/**
      - .github/workflows/rocky_makelang.yml
  pull_request:
    paths:
      - rocky/**
      - .github/workflows/rocky_makelang.yml

jobs:
  makelang:
    permissions:
      contents: read
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: 3.13
          cache: pip

      - name: Install GNU gettext utilities
        run: sudo apt-get install -y --no-install-recommends gettext

      - name: Install requirements.txt
        run: |
          grep -v git+https:// requirements.txt | pip install -r /dev/stdin
          grep git+https:// requirements.txt | pip install -r /dev/stdin
        working-directory: ./rocky

      - name: Install Octopoes
        run: pip install wheel setuptools && cd octopoes && python setup.py bdist_wheel && pip install dist/octopoes*.whl

      - name: Generate the .pot file if source strings changed
        run: make lang
        working-directory: ./rocky
        env:
          BYTES_API: http://bytes:8000
          BYTES_PASSWORD: password
          BYTES_USERNAME: username
          KATALOGUS_API: http://katalogus:8000
          OCTOPOES_API: http://octopoes_api:80
          SCHEDULER_API: http://scheduler:8000
          SECRET_KEY: whatever

      - name: Check if at least one source string changed
        run: |
          changed_lines=$(git diff --unified=0 rocky/rocky/locale/django.pot | grep -E -o '^[+-](msgid|msgstr)' | wc -l)
          if [ "$changed_lines" -ge 1 ]; then
            echo "More than just POT-Creation-Date changed in django.pot"
            exit 1
          fi


================================================
FILE: .github/workflows/rocky_tests.yml
================================================
name: Rocky Run the test suite

on:
  push:
    branches:
      - "main"
      - "release-*"
    tags:
      - "*"
    paths:
      - octopoes/**
      - rocky/**
      - .github/workflows/rocky_tests.yml
  pull_request:
    paths:
      - octopoes/**
      - rocky/**
      - .github/workflows/rocky_tests.yml

jobs:
  test:
    permissions:
      contents: read
    strategy:
      fail-fast: false
      matrix:
        version: ["3.10", "3.11", "3.12", "3.13"]

    runs-on: ubuntu-24.04

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Build image
        run: DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose -f rocky/.ci/docker-compose.yml build --build-arg USER_UID="$(id -u)" --build-arg USER_GID="$(id -g)" --build-arg PYTHON_VERSION=${{ matrix.version }} rocky_tests rocky_integration

      - name: Run tests
        run: DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose -f rocky/.ci/docker-compose.yml run --rm rocky_tests

      - name: Run integration tests
        run: DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose -f rocky/.ci/docker-compose.yml run --rm rocky_integration


================================================
FILE: .github/workflows/sonar-cloud.yml
================================================
name: SonarCloud

on:
  #workflow_dispatch:

  #push:
  #  branches:
  #    - "main"
  #pull_request:

jobs:
  octopoes-tests:
    permissions:
      contents: read
    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/octopoes-tests.yml@main
  bytes-tests:
    permissions:
      contents: read
    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/bytes-tests.yml@main
  mula-tests:
    permissions:
      contents: read
    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/mula-tests.yml@main
  rocky-tests:
    permissions:
      contents: read
    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/rocky-tests.yml@main
  boefjes-tests:
    permissions:
      contents: read
    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/boefjes-tests.yml@main

  fix-coverage-reports:
    permissions:
      contents: read
    runs-on: ubuntu-24.04

    needs:
      - octopoes-tests
      - mula-tests
      - bytes-tests
      - rocky-tests
      - boefjes-tests

    strategy:
      matrix:
        module:
          - name: octopoes
            prefix_path: "octopoes/"
          - name: mula
            prefix_path: "mula/"
          - name: bytes
            prefix_path: "bytes/"
          - name: rocky
            prefix_path: "rocky/"
          - name: boefjes
            prefix_path: "boefjes/"

    steps:
      - name: Checkout coverage file fix script
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          sparse-checkout: .github/scripts/coverage_file_fixer.py

      - name: Download coverage file
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: ${{ matrix.module['name'] }}-coverage
          path: ${{ matrix.module['name'] }}-coverage

      - name: Fix coverage report sources
        uses: Mudlet/xmlstarlet-action@9866e85e774e0fb50bc49de15274d005b5a69f0e # master
        with:
          args: edit --inplace --update "coverage/sources" --value "/github/workspace/${{ matrix.module['name'] }}/" "${{ matrix.module['name'] }}-coverage/coverage.xml"

      - name: Fix coverage file
        run: python "${{ github.workspace }}/.github/scripts/coverage_file_fixer.py" "${{ matrix.module['name'] }}-coverage/coverage.xml" "${{ matrix.module['prefix_path'] }}"

      - name: Upload fixed coverage file
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: "${{ matrix.module['name'] }}-coverage-fixed"
          path: "${{ matrix.module['name'] }}-coverage/coverage.xml"

  sonar-cloud:
    permissions:
      contents: read
      pull-requests: write
      security-events: write
    runs-on: ubuntu-24.04

    needs:
      - fix-coverage-reports

    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0

      - name: Download artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: "*-coverage-fixed"

      - name: SonarCloud
        uses: SonarSource/sonarqube-scan-action@59db25f34e16620e48ab4bb9e4a5dce155cb5432 # v8.0.0
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}


================================================
FILE: .github/workflows/test_debian_packages_on_ubuntu.yml
================================================
name: Test installing the debian packages

on:
  push:
    tags:
      - v*
    branches:
      - "release**"
      - "main"
  pull_request:
    branches:
      - "release**"
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  build-packages:
    permissions:
      contents: read
    strategy:
      matrix:
        dist: [ubuntu22.04]
        package: [bytes, boefjes, rocky, octopoes, mula]
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Get version from release tag or generate one
        run: |
          if [ ${GITHUB_REF_TYPE} = "tag" ]; then
            echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV
          else
            pip install setuptools-scm==7.1.0
            echo "RELEASE_VERSION=$(python -m setuptools_scm)" >> $GITHUB_ENV
          fi

      - name: Run debian package build
        uses: maus007/docker-run-action-fork@5ddaad0f7eedd03f64e412b1931852bd3031b273
        with:
          run: packaging/scripts/build-debian-package.sh
          registry: docker.underdark.nl
          image: docker.underdark.nl/librekat/openkat-${{ matrix.dist }}-build-image:latest
          username: ${{ secrets.librekat_user }}
          password: ${{ secrets.librekat_password }}
          options: -v ${{ github.workspace }}/${{ matrix.package }}:/app
            -v ${{ github.workspace }}/octopoes:/octopoes
            -e REPOSITORY=${{ github.repository }}
            -e RELEASE_VERSION=${{ env.RELEASE_VERSION }}
            -e RELEASE_TAG=${{ env.RELEASE_TAG }}
            -e PKG_NAME=kat-${{ matrix.package }}
            --workdir /app

      - name: Upload .deb to artifacts
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: kat-${{ matrix.package }}_${{ env.RELEASE_VERSION }}_${{ matrix.dist }}.deb
          path: ${{ matrix.package }}/build/kat-${{ matrix.package }}_${{ env.RELEASE_VERSION }}_amd64.deb

  test-deb-install:
    permissions:
      contents: read
    needs: build-packages
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [ubuntu-22.04]

    env:
      PGPASSWORD: postgres # No password prompt

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Get version from release tag or generate one
        run: |
          if [ ${GITHUB_REF_TYPE} = "tag" ]; then
            echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV
          else
            pip install setuptools-scm==7.1.0
            echo "RELEASE_VERSION=$(python -m setuptools_scm)" >> $GITHUB_ENV
          fi

      - name: Set distro name (match build matrix.dist)
        run: echo "DIST_NAME=${{ matrix.os }}" | sed 's/-//' >> $GITHUB_ENV

      - name: Install dependencies Docker and rabbitmq
        run: sudo apt-get update && sudo apt-get install -y docker.io containerd runc rabbitmq-server postgresql

      - name: Start postgresql
        run: |
          sudo systemctl start postgresql.service

      - name: Prepare rabbitmq configurations
        run: |
          echo "export ERL_EPMD_ADDRESS=127.0.0.1" | sudo tee -a /etc/rabbitmq/rabbitmq-env.conf
          echo "export NODENAME=rabbit@localhost" | sudo tee -a /etc/rabbitmq/rabbitmq-env.conf
          sudo systemctl stop rabbitmq-server
          sudo epmd -kill
          echo "listeners.tcp.local = 127.0.0.1:5672" | sudo tee -a /etc/rabbitmq/rabbitmq.conf
          echo "[{kernel,[ {inet_dist_use_interface,{127,0,0,1}}]}]." | sudo tee -a /etc/rabbitmq/advanced.config

      - name: Start rabbitmq
        run: |
          sudo systemctl start rabbitmq-server

      - name: Wait until postgresql is healthy
        run: |
          for i in {1..10}; do pg_isready -h localhost && break || sleep 1 ; done

      - name: Prepare postgres service Rocky
        run: |
          sudo -u postgres createdb rocky_db
          sudo -u postgres createuser rocky
          sudo -u postgres psql -c "GRANT ALL ON DATABASE rocky_db TO rocky;"
          sudo -u postgres psql -c "ALTER USER rocky WITH PASSWORD 'postgres';"

      - name: Prepare postgres service Katalogus
        run: |
          sudo -u postgres createdb katalogus_db
          sudo -u postgres createuser katalogus
          sudo -u postgres psql -c "GRANT ALL ON DATABASE katalogus_db TO katalogus;"
          sudo -u postgres psql -c "ALTER USER katalogus WITH PASSWORD 'postgres';"

      - name: Prepare postgres service Bytes
        run: |
          sudo -u postgres createdb bytes_db
          sudo -u postgres createuser bytes
          sudo -u postgres psql -c "GRANT ALL ON DATABASE bytes_db TO bytes;"
          sudo -u postgres psql -c "ALTER USER bytes WITH PASSWORD 'postgres';"

      - name: Prepare postgres service Mula
        run: |
          sudo -u postgres createdb mula_db
          sudo -u postgres createuser mula
          sudo -u postgres psql -c "GRANT ALL ON DATABASE mula_db TO mula;"
          sudo -u postgres psql -c "ALTER USER mula WITH PASSWORD 'postgres';"

      - name: Wait until rabbitmq is healthy
        run: |
          for i in {1..10}; do sudo rabbitmq-diagnostics -q ping && break || sleep 1 ; done

      - name: Create kat vhost in rabbitmq
        run: |
          sudo rabbitmqctl add_user kat rabbit
          sudo rabbitmqctl add_vhost kat
          sudo rabbitmqctl set_permissions -p "kat" "kat" ".*" ".*" ".*"

      - name: Download Bytes artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: kat-bytes_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb

      - name: Download Boefjes artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: kat-boefjes_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb

      - name: Download Rocky artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: kat-rocky_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb

      - name: Download Mula artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: kat-mula_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb

      - name: Download Octopoes artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: kat-octopoes_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb

      - name: Get the OpenKAT artifacts
        run: |
          curl -Ls https://github.com/underdarknl/xtdb-http-multinode/releases/download/v1.1.2/xtdb-http-multinode_1.1.2_all.deb --output xtdb-http-multinode.deb;

      - name: Install the artifacts
        run: |
          sudo apt install ./kat-*.deb ./xtdb-http-multinode.deb

      - name: Update configs for rabbitmq
        run: |
          sudo sed -i "s/QUEUE_URI=/QUEUE_URI=amqp:\/\/kat:rabbit@localhost:5672\/kat/g" /etc/kat/mula.conf
          sudo sed -i "s/QUEUE_URI=/QUEUE_URI=amqp:\/\/kat:rabbit@localhost:5672\/kat/g" /etc/kat/bytes.conf
          sudo sed -i "s/QUEUE_URI=/QUEUE_URI=amqp:\/\/kat:rabbit@localhost:5672\/kat/g" /etc/kat/boefjes.conf
          sudo sed -i "s/QUEUE_URI=/QUEUE_URI=amqp:\/\/kat:rabbit@localhost:5672\/kat/g" /etc/kat/octopoes.conf

      - name: Migrations for Rocky
        run: |
          sudo sed -i "s/ROCKY_DB_PASSWORD=/ROCKY_DB_PASSWORD=postgres/g" /etc/kat/rocky.conf
          sudo -u kat rocky-cli migrate
          sudo -u kat rocky-cli loaddata /usr/share/kat-rocky/OOI_database_seed.json

      - name: Migrations for Katalogus
        run: |
          sudo sed -i "s/KATALOGUS_DB_URI=/KATALOGUS_DB_URI=postgresql:\/\/katalogus:postgres@localhost\/katalogus_db/g" /etc/kat/boefjes.conf
          sudo -u kat update-katalogus-db

      - name: Migrations for Bytes
        run: |
          sudo sed -i "s/BYTES_DB_URI=/BYTES_DB_URI=postgresql:\/\/bytes:postgres@localhost\/bytes_db/g" /etc/kat/bytes.conf
          sudo -u kat update-bytes-db

      - name: Migrations for Mula
        run: |
          sudo sed -i "s/SCHEDULER_DB_URI=/SCHEDULER_DB_URI=postgresql:\/\/mula:postgres@localhost\/mula_db/g" /etc/kat/mula.conf
          sudo -u kat update-mula-db

      - name: Setup Bytes credentials
        run: |
          sudo sed -i "s/BYTES_PASSWORD=\$/BYTES_PASSWORD=$(sudo grep BYTES_PASSWORD /etc/kat/bytes.conf | awk -F'=' '{ print $2 }')/" /etc/kat/rocky.conf
          sudo sed -i "s/BYTES_PASSWORD=\$/BYTES_PASSWORD=$(sudo grep BYTES_PASSWORD /etc/kat/bytes.conf | awk -F'=' '{ print $2 }')/" /etc/kat/boefjes.conf
          sudo sed -i "s/BYTES_PASSWORD=\$/BYTES_PASSWORD=$(sudo grep BYTES_PASSWORD /etc/kat/bytes.conf | awk -F'=' '{ print $2 }')/" /etc/kat/mula.conf

      - name: Restart KAT
        run: sudo systemctl restart kat-rocky kat-rocky-worker kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker

      - name: Setup accounts in Rocky
        run: |
          DJANGO_SUPERUSER_PASSWORD=robotpassword sudo -E -u kat rocky-cli createsuperuser --noinput --email robot@localhost --full_name "Mr. Robot"
          sudo -u kat rocky-cli setup_dev_account

      - name: Check Bytes API health or print response and logs
        run: |
          for i in {1..15}; do curl -s http://localhost:8002/health | jq .healthy | grep true -q && s=0 && break || s=$? && sleep 1 ; done
          if [ $s != 0 ]; then echo $(curl -v http://localhost:8002/health) || true && journalctl --no-pager -u kat-bytes.service && exit $s ; fi

      - name: Check Katalogus API health or print response and logs
        run: |
          for i in {1..15}; do curl -s http://localhost:8003/health | jq .healthy | grep true -q && s=0 && break || s=$? && sleep 1 ; done
          if [ $s != 0 ]; then echo $(curl -v http://localhost:8003/health) || true && journalctl --no-pager -u kat-katalogus.service && exit $s ; fi

      - name: Check Scheduler API health or print response and logs
        run: |
          for i in {1..15}; do curl -s http://localhost:8004/health | jq .healthy | grep true -q && s=0 && break || s=$? && sleep 1 ; done
          if [ $s != 0 ]; then echo $(curl -v http://localhost:8004/health) || true && journalctl --no-pager -u kat-mula.service && exit $s ; fi

      - name: Check XTDB health or print response and logs
        run: |
          for i in {1..30}; do curl -s -H "Accept: application/edn" http://localhost:3000/_dev/_xtdb/test/status && s=0 && break || s=$? && sleep 1 ; done
          if [ $s != 0 ]; then echo $(curl -s -H "Accept: application/edn" http://localhost:3000/_dev/_xtdb/test/status) || true && journalctl --no-pager -u xtdb-http-multinode.service && exit $s ; fi

      - name: Create _dev node in Octopoes
        run: curl -s -X POST http://localhost:8001/_dev/node

      - name: Check Octopoes API health or print response and logs
        run: |
          for i in {1..15}; do curl -s http://localhost:8001/_dev/health | jq .healthy | grep true -q && s=0 && break || s=$? && sleep 1 ; done
          if [ $s != 0 ]; then echo $(curl -v http://localhost:8001/_dev/health) || true && journalctl --no-pager -u kat-octopoes.service && exit $s ; fi

      - name: Set up Python 3.13
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: pip

      - name: Install Robot Framework
        run: pip3 install robotframework robotframework-browser robotframework-debuglibrary robotframework-otp robotframework-postgresqldb pyotp

      - name: Initialize rfbrowser
        run: rfbrowser init

      - name: Run Robot Full Onboarding Flow
        run: robot -d rocky/tests/robot/results-ci -v headless:true rocky/tests/robot/ci

      - name: Upload Robot Framework reports
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        if: always()
        with:
          name: rf-results-ci
          path: /home/runner/work/nl-kat-coordination/nl-kat-coordination/rocky/tests/robot/results*


================================================
FILE: .gitignore
================================================

# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode
# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,node,react,intellij,vscode

### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/
.vscode

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn.  Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
*.iml
*.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721

# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr

# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/

# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml

# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/

# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$

# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml

### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env*.local
*.env

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
pytestdebug.log

# Translations
*.mo

# Django stuff:
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/
doc/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
pythonenv*

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# profiling data
.prof

### react ###
.DS_*
**/*.backup.*
**/*.back.*

node_modules

*.sublime*

psd
thumb
sketch

### vscode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace

### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db

# Dump file
*.stackdump

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp

# Windows shortcuts
*.lnk

# Swap files
*.swp

# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode

/bytes/bytes-data
/nl-kat-bytes/bytes-data
/errors
*.tar.gz
.password-store
.envrc
deployment/github_config.sh

nl-kat-*
/_version.py

# Automatically generated markdown files for the environment variables
docs/source/installation-and-deployment/environment-settings/boefjes.md
docs/source/installation-and-deployment/environment-settings/bytes.md
docs/source/installation-and-deployment/environment-settings/mula.md
docs/source/installation-and-deployment/environment-settings/octopoes.md

docs/source/_static/d3.min.js
docs/source/_static/mermaid.min.js

# rpki cache
/boefjes/boefjes/plugins/kat_rpki/rpki.json
/boefjes/boefjes/plugins/kat_rpki/rpki-meta.json
/boefjes/boefjes/plugins/kat_rpki/bgp.jsonl
/boefjes/boefjes/plugins/kat_rpki/bgp-meta.json

# ignore test tmp files
/boefjes/tests/modules/**/boefje.json.tmp

*.pstat
**/.cache*


================================================
FILE: .gitpod.yml
================================================
# Reference: https://www.gitpod.io/docs/references/gitpod-yml

tasks:
  - init: make

ports:
  - port: 8000
    onOpen: open-preview


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: trailing-whitespace
      - id: mixed-line-ending
        args: ["--fix=lf"]
      - id: detect-private-key
        exclude: ^boefjes/boefjes/plugins/kat_cve_2023_34039/keys/
      - id: check-case-conflict
      - id: check-yaml
      - id: check-json
      - id: check-toml
      - id: debug-statements
        exclude: |
          (?x)(
          ^boefjes/tools |
          ^octopoes/tools
          )
      - id: end-of-file-fixer
        exclude: |
          (?x)(
          \.svcg$ |
          ^boefjes/tests/examples/rdns-nxdomain.txt$ |
          ^boefjes/tests/examples/raw/
          )
      - id: fix-byte-order-marker
      - id: pretty-format-json
        args: ["--autofix", "--no-ensure-ascii", "--no-sort-keys"]
        exclude: |
          (?x)(
          ^boefjes/boefjes/plugins/kat_wappalyzer/technologies.json |
          )

  - repo: https://github.com/abravalheri/validate-pyproject
    rev: v0.23
    hooks:
      - id: validate-pyproject
        files: pyproject.toml$

  - repo: https://github.com/rstcheck/rstcheck
    rev: v6.2.4
    hooks:
      - id: rstcheck
        # https://github.com/rstcheck/rstcheck-core/issues/4
        args:
          [
            "--ignore-messages",
            "Hyperlink target .* is not referenced",
            "--ignore-directives",
            "mermaid,automodule",
          ]
        additional_dependencies: ["rstcheck[sphinx]", "autodoc-pydantic==2.1.0"]

  - repo: https://github.com/MarketSquare/robotframework-tidy
    rev: "4.14.0"
    hooks:
      - id: robotidy

  - repo: https://github.com/jendrikseipp/vulture
    rev: v2.13
    hooks:
      - id: vulture
        exclude: |
          /tests/

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: "v0.8.1"
    hooks:
      - id: ruff
      - id: ruff-format

  - repo: https://github.com/asottile/pyupgrade
    rev: v3.19.0
    hooks:
      - id: pyupgrade
        args: [--py310-plus]

  - repo: https://github.com/adamchainz/django-upgrade
    rev: 1.22.1
    hooks:
      - id: django-upgrade
        args: [--target-version, "5.0"]

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.13.0
    hooks:
      - id: mypy
        additional_dependencies:
          - types-pyyaml
          - types-cachetools
          - types-retry
          - pydantic
          - pynacl
          - httpx
          - types-python-dateutil
          - types-requests
          - types-croniter
          - boto3-stubs[s3]
        exclude: |
          (?x)(
          ^boefjes/tools |
          ^mula/whitelist\.py$ |
          ^mula/scripts |
          ^octopoes/tools |
          ^rocky/whitelist\.py$ |
          /tests/ |
          docs/source/conf\.py$ |
          setup\.py$
          )

  - repo: https://github.com/codespell-project/codespell
    rev: v2.3.0
    hooks:
      - id: codespell
        additional_dependencies: ["tomli"]
        args: ["-L", "lama", "--ignore-regex", ".{1024}|.*codespell-ignore.*"]
        exclude: |
          (?x)(
          \.po$ |
          \.xml$ |
          \.svg$ |
          poetry.lock$ |
          pyproject.toml$ |
          requirements-.*.txt$ |
          retirejs.json$ |
          ^boefjes/boefjes/plugins/kat_fierce/lists |
          ^boefjes/boefjes/plugins/kat_wappalyzer/data/.*.json |
          ^boefjes/tests/examples/inputs/cve-result-without-cvss.json |
          ^boefjes/tests/examples |
          ^rocky/assets/js/vendor |
          ^rocky/assets/css/themes/soft/fonts/tabler-icons/tabler-icons.scss$ |
          ^rocky/tests/stubs |
          ^rocky/reports/report_types/aggregate_organisation_report |
          ^rocky/reports/report_types/multi_organization_report |
          ^docs/source/_static |
          ^boefjes/boefjes/plugins/kat_cve_2023_34039/keys/ |
          ^boefjes/boefjes/plugins/kat_rpki/rpki.json
          )

  - repo: https://github.com/Riverside-Healthcare/djLint
    rev: v1.36.3
    hooks:
      - id: djlint-reformat-django
        files: |
          (?x)(
          ^rocky/.*/templates/.*$ |
          ^rocky/reports/report_types/.*/.*\.html
          )
        exclude: '^rocky/rocky/templates/admin/.*\.html$'

      - id: djlint-django
        files: |
          (?x)(
          ^rocky/.*/templates/.*$ |
          ^rocky/reports/report_types/.*/.*\.html
          )
        exclude: '^rocky/rocky/templates/admin/.*\.html$'

  - repo: https://github.com/thibaudcolas/pre-commit-stylelint
    rev: v16.10.0
    hooks:
      - id: stylelint
        args: [--fix]
        additional_dependencies:
          - stylelint@15.10.1
          - stylelint-config-standard-scss@10.0.0
        files: "^(rocky\/assets\/css\/|docs\/source\/).*.(css|scss|sass)$"

  - repo: https://github.com/shellcheck-py/shellcheck-py
    rev: v0.10.0.1
    hooks:
      - id: shellcheck
        args: ["-e", "SC1091"]

  - repo: https://github.com/scop/pre-commit-shfmt
    rev: v3.10.0-1
    hooks:
      - id: shfmt
        args: ["-w", "-s", "-i", "4", "-sr"]

  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v4.0.0-alpha.8
    hooks:
      - id: prettier
        additional_dependencies:
          - prettier@3.2.5 # SEE: https://github.com/pre-commit/pre-commit/issues/3133
        exclude: |
          (?x)(
          \.html$ |
          \.json$ |
          \.min\.js$ |
          ^rocky/assets/css/themes/soft/fonts |
          ^rocky/assets/vendors |
          ^docs/source/_static
          )


================================================
FILE: .stylelintrc.json
================================================
{
  "extends": "stylelint-config-standard-scss",
  "ignoreFiles": [
    "rocky/assets/css/themes/soft/fonts/tabler-icons/tabler-icons.scss"
  ],
  "rules": {
    "number-max-precision": 5,
    "color-hex-length": "long",
    "declaration-block-no-redundant-longhand-properties": null,
    "no-descending-specificity": null,
    "scss/operator-no-newline-after": null,
    "font-family-no-missing-generic-family-keyword": [
      true,
      {
        "ignoreFontFamilies": [
          "RO Icons",
          "tabler-icons",
          "kat-icons"
        ]
      }
    ]
  }
}


================================================
FILE: CONTRIBUTING.rst
================================================
============
Contributing
============

Thank you, dear developer, who is considering to help out with OpenKAT! Feel welcome. If you want to get in touch, please do so!

Documentation
=============

We keep `our documentation here <https://docs.openkat.nl>`_, generated from our github repo.

Guidelines
==========

`Our contribution guidelines <https://docs.openkat.nl/developer-documentation/contributor/index.html>`_ might help you find your way.

Contact
=======

`Get in touch <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/README.rst#contact>`_ with our dev team or community managers here.


================================================
FILE: LICENSE
================================================
                      EUROPEAN UNION PUBLIC LICENCE v. 1.2
                      EUPL © the European Union 2007, 2016

This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
below) which is provided under the terms of this Licence. Any use of the Work,
other than as authorised under this Licence is prohibited (to the extent such
use is covered by a right of the copyright holder of the Work).

The Work is provided under the terms of this Licence when the Licensor (as
defined below) has placed the following notice immediately following the
copyright notice for the Work:

        Licensed under the EUPL

or has expressed by any other means his willingness to license under the EUPL.

1. Definitions

In this Licence, the following terms have the following meaning:

- ‘The Licence’: this Licence.

- ‘The Original Work’: the work or software distributed or communicated by the
  Licensor under this Licence, available as Source Code and also as Executable
  Code as the case may be.

- ‘Derivative Works’: the works or software that could be created by the
  Licensee, based upon the Original Work or modifications thereof. This Licence
  does not define the extent of modification or dependence on the Original Work
  required in order to classify a work as a Derivative Work; this extent is
  determined by copyright law applicable in the country mentioned in Article 15.

- ‘The Work’: the Original Work or its Derivative Works.

- ‘The Source Code’: the human-readable form of the Work which is the most
  convenient for people to study and modify.

- ‘The Executable Code’: any code which has generally been compiled and which is
  meant to be interpreted by a computer as a program.

- ‘The Licensor’: the natural or legal person that distributes or communicates
  the Work under the Licence.

- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
  Licence, or otherwise contributes to the creation of a Derivative Work.

- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
  the Work under the terms of the Licence.

- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
  renting, distributing, communicating, transmitting, or otherwise making
  available, online or offline, copies of the Work or providing access to its
  essential functionalities at the disposal of any other natural or legal
  person.

2. Scope of the rights granted by the Licence

The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
sublicensable licence to do the following, for the duration of copyright vested
in the Original Work:

- use the Work in any circumstance and for all usage,
- reproduce the Work,
- modify the Work, and make Derivative Works based upon the Work,
- communicate to the public, including the right to make available or display
  the Work or copies thereof to the public and perform publicly, as the case may
  be, the Work,
- distribute the Work or copies thereof,
- lend and rent the Work or copies thereof,
- sublicense rights in the Work or copies thereof.

Those rights can be exercised on any media, supports and formats, whether now
known or later invented, as far as the applicable law permits so.

In the countries where moral rights apply, the Licensor waives his right to
exercise his moral right to the extent allowed by law in order to make effective
the licence of the economic rights here above listed.

The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
any patents held by the Licensor, to the extent necessary to make use of the
rights granted on the Work under this Licence.

3. Communication of the Source Code

The Licensor may provide the Work either in its Source Code form, or as
Executable Code. If the Work is provided as Executable Code, the Licensor
provides in addition a machine-readable copy of the Source Code of the Work
along with each copy of the Work that the Licensor distributes or indicates, in
a notice following the copyright notice attached to the Work, a repository where
the Source Code is easily and freely accessible for as long as the Licensor
continues to distribute or communicate the Work.

4. Limitations on copyright

Nothing in this Licence is intended to deprive the Licensee of the benefits from
any exception or limitation to the exclusive rights of the rights owners in the
Work, of the exhaustion of those rights or of other applicable limitations
thereto.

5. Obligations of the Licensee

The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:

Attribution right: The Licensee shall keep intact all copyright, patent or
trademarks notices and all notices that refer to the Licence and to the
disclaimer of warranties. The Licensee must include a copy of such notices and a
copy of the Licence with every copy of the Work he/she distributes or
communicates. The Licensee must cause any Derivative Work to carry prominent
notices stating that the Work has been modified and the date of modification.

Copyleft clause: If the Licensee distributes or communicates copies of the
Original Works or Derivative Works, this Distribution or Communication will be
done under the terms of this Licence or of a later version of this Licence
unless the Original Work is expressly distributed only under this version of the
Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
(becoming Licensor) cannot offer or impose any additional terms or conditions on
the Work or Derivative Work that alter or restrict the terms of the Licence.

Compatibility clause: If the Licensee Distributes or Communicates Derivative
Works or copies thereof based upon both the Work and another work licensed under
a Compatible Licence, this Distribution or Communication can be done under the
terms of this Compatible Licence. For the sake of this clause, ‘Compatible
Licence’ refers to the licences listed in the appendix attached to this Licence.
Should the Licensee's obligations under the Compatible Licence conflict with
his/her obligations under this Licence, the obligations of the Compatible
Licence shall prevail.

Provision of Source Code: When distributing or communicating copies of the Work,
the Licensee will provide a machine-readable copy of the Source Code or indicate
a repository where this Source will be easily and freely available for as long
as the Licensee continues to distribute or communicate the Work.

Legal Protection: This Licence does not grant permission to use the trade names,
trademarks, service marks, or names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the copyright notice.

6. Chain of Authorship

The original Licensor warrants that the copyright in the Original Work granted
hereunder is owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.

Each Contributor warrants that the copyright in the modifications he/she brings
to the Work are owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.

Each time You accept the Licence, the original Licensor and subsequent
Contributors grant You a licence to their contributions to the Work, under the
terms of this Licence.

7. Disclaimer of Warranty

The Work is a work in progress, which is continuously improved by numerous
Contributors. It is not a finished work and may therefore contain defects or
‘bugs’ inherent to this type of development.

For the above reason, the Work is provided under the Licence on an ‘as is’ basis
and without warranties of any kind concerning the Work, including without
limitation merchantability, fitness for a particular purpose, absence of defects
or errors, accuracy, non-infringement of intellectual property rights other than
copyright as stated in Article 6 of this Licence.

This disclaimer of warranty is an essential part of the Licence and a condition
for the grant of any rights to the Work.

8. Disclaimer of Liability

Except in the cases of wilful misconduct or damages directly caused to natural
persons, the Licensor will in no event be liable for any direct or indirect,
material or moral, damages of any kind, arising out of the Licence or of the use
of the Work, including without limitation, damages for loss of goodwill, work
stoppage, computer failure or malfunction, loss of data or any commercial
damage, even if the Licensor has been advised of the possibility of such damage.
However, the Licensor will be liable under statutory product liability laws as
far such laws apply to the Work.

9. Additional agreements

While distributing the Work, You may choose to conclude an additional agreement,
defining obligations or services consistent with this Licence. However, if
accepting obligations, You may act only on your own behalf and on your sole
responsibility, not on behalf of the original Licensor or any other Contributor,
and only if You agree to indemnify, defend, and hold each Contributor harmless
for any liability incurred by, or claims asserted against such Contributor by
the fact You have accepted any warranty or additional liability.

10. Acceptance of the Licence

The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
placed under the bottom of a window displaying the text of this Licence or by
affirming consent in any other similar way, in accordance with the rules of
applicable law. Clicking on that icon indicates your clear and irrevocable
acceptance of this Licence and all of its terms and conditions.

Similarly, you irrevocably accept this Licence and all of its terms and
conditions by exercising any rights granted to You by Article 2 of this Licence,
such as the use of the Work, the creation by You of a Derivative Work or the
Distribution or Communication by You of the Work or copies thereof.

11. Information to the public

In case of any Distribution or Communication of the Work by means of electronic
communication by You (for example, by offering to download the Work from a
remote location) the distribution channel or media (for example, a website) must
at least provide to the public the information requested by the applicable law
regarding the Licensor, the Licence and the way it may be accessible, concluded,
stored and reproduced by the Licensee.

12. Termination of the Licence

The Licence and the rights granted hereunder will terminate automatically upon
any breach by the Licensee of the terms of the Licence.

Such a termination will not terminate the licences of any person who has
received the Work from the Licensee under the Licence, provided such persons
remain in full compliance with the Licence.

13. Miscellaneous

Without prejudice of Article 9 above, the Licence represents the complete
agreement between the Parties as to the Work.

If any provision of the Licence is invalid or unenforceable under applicable
law, this will not affect the validity or enforceability of the Licence as a
whole. Such provision will be construed or reformed so as necessary to make it
valid and enforceable.

The European Commission may publish other linguistic versions or new versions of
this Licence or updated versions of the Appendix, so far this is required and
reasonable, without reducing the scope of the rights granted by the Licence. New
versions of the Licence will be published with a unique version number.

All linguistic versions of this Licence, approved by the European Commission,
have identical value. Parties can take advantage of the linguistic version of
their choice.

14. Jurisdiction

Without prejudice to specific agreement between parties,

- any litigation resulting from the interpretation of this License, arising
  between the European Union institutions, bodies, offices or agencies, as a
  Licensor, and any Licensee, will be subject to the jurisdiction of the Court
  of Justice of the European Union, as laid down in article 272 of the Treaty on
  the Functioning of the European Union,

- any litigation arising between other parties and resulting from the
  interpretation of this License, will be subject to the exclusive jurisdiction
  of the competent court where the Licensor resides or conducts its primary
  business.

15. Applicable Law

Without prejudice to specific agreement between parties,

- this Licence shall be governed by the law of the European Union Member State
  where the Licensor has his seat, resides or has his registered office,

- this licence shall be governed by Belgian law if the Licensor has no seat,
  residence or registered office inside a European Union Member State.

Appendix

‘Compatible Licences’ according to Article 5 EUPL are:

- GNU General Public License (GPL) v. 2, v. 3
- GNU Affero General Public License (AGPL) v. 3
- Open Software License (OSL) v. 2.1, v. 3.0
- Eclipse Public License (EPL) v. 1.0
- CeCILL v. 2.0, v. 2.1
- Mozilla Public Licence (MPL) v. 2
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
  works other than software
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
  Reciprocity (LiLiQ-R+).

The European Commission may update this Appendix to later versions of the above
licences without producing a new version of the EUPL, as long as they provide
the rights granted in Article 2 of this Licence and protect the covered Source
Code from exclusive appropriation.

All other changes or additions to this Appendix require the production of a new
EUPL version.


================================================
FILE: Makefile
================================================
SHELL := bash
.ONESHELL:
.NOTPARALLEL:

# use HIDE to run commands invisibly, unless VERBOSE defined
HIDE:=$(if $(VERBOSE),,@)
UNAME := $(shell uname)

.PHONY: kat update reset up stop down clean fetch pull upgrade env-if-empty env build debian-build-image ubuntu-build-image docs upgraderequirements requirements

# Export Docker buildkit options
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1

# We can't really return an error here, so if settings-doc fails we delete the
# file which will result in sphinx-build returning an error later on
define build-settings-doc
	echo "# $(4)" > docs/source/installation-and-deployment/environment-settings/$(3).md
	DOCS=True PYTHONPATH=./$(1) settings-doc generate \
	-f markdown -m $(2) \
	--templates docs/settings-doc-templates \
	>> docs/source/installation-and-deployment/environment-settings/$(3).md || exit 1
endef


# Build and bring up all containers (default target)
kat: env-if-empty build up
	@echo
	@echo "The KAT frontend is running at http://localhost:8000,"
	@echo "An initial superuser has been created"
	@echo "The username is stored in DJANGO_SUPERUSER_EMAIL in the .env-default file."
	@echo "run 'grep 'DJANGO_SUPERUSER_EMAIL' .env-defaults' to find it."
	@echo "The related password can be found as DJANGO_SUPERUSER_PASSWORD in the .env file."
	@echo "run 'grep 'DJANGO_SUPERUSER_PASSWORD' .env' to find it."
	@echo
	@echo "WARNING: This is a development environment, do not use in production!"
	@echo "See https://docs.openkat.nl/installation-and-deployment/production-docker-environment.html for production"
	@echo "installation instructions."

# Remove containers, update using git pull and bring up containers
update: down pull kat

# Remove all containers and volumes, and bring containers up again (data loss!)
reset: clean kat

# Bring up containers
up:
	docker compose up --detach

# Stop containers
stop:
	-docker compose stop

# Remove containers but not volumes (no data loss)
down:
	-docker compose down

# Remove containers and all volumes (data loss!)
clean:
	-docker compose down --timeout 0 --volumes --remove-orphans
	-rm -Rf rocky/node_modules rocky/assets/dist rocky/.parcel-cache rocky/static

# Fetch the latest changes from the Git remote
fetch:
	git fetch --all --prune --tags

# Pull the latest changes from the default upstream
pull:
	git pull
	docker compose pull

# Upgrade to the latest release without losing persistent data. Usage: `make upgrade version=v1.5.0` (version is optional)
VERSION?=$(shell curl -sSf "https://api.github.com/repos/SSC-ICT-Innovatie/nl-kat-coordination/tags" | jq -r '[.[].name | select(. | contains("rc") | not)][0]')
upgrade: down fetch
	git checkout $(VERSION)
	make kat

# Create .env file only if it does not exist
env-if-empty:
ifeq ("$(wildcard .env)","")
	make env
endif

# Create .env file from the env-dist with randomly generated credentials from vars annotated by "{%EXAMPLE_VAR}"
env:
	cp .env-dist .env
	echo "Initializing .env with random credentials"
ifeq ($(UNAME), Darwin)  # Different sed on MacOS
	$(HIDE) grep -o "{%\([_A-Z]*\)}" .env-dist | sort -u | while read v; do sed -i '' "s/$$v/$$(openssl rand -hex 25)/g" .env; done
else
	$(HIDE) grep -o "{%\([_A-Z]*\)}" .env-dist | sort -u | while read v; do sed -i "s/$$v/$$(openssl rand -hex 25)/g" .env; done
endif

# Build will prepare all services: migrate them, seed them, etc.
build:
ifeq ($(UNAME),Darwin)
	docker compose build --pull --parallel --build-arg USER_UID="$$(id -u)"
else
	docker compose build --pull --parallel --build-arg USER_UID="$$(id -u)" --build-arg USER_GID="$$(id -g)"
endif
	make -C rocky build
	make -C boefjes build

# Build Debian 11 build image
debian12-build-image:
	docker build -t kat-debian12-build-image packaging/debian12

# Build Ubuntu 22.04 build image
ubuntu22.04-build-image:
	docker build -t kat-ubuntu22.04-build-image packaging/ubuntu22.04

CHECKSUM_CMD = $(if $(filter $(UNAME), Darwin), shasum -a 256, sha256sum --quiet)

docs:
	$(call build-settings-doc,octopoes,octopoes.config.settings,octopoes,Octopoes)
	$(call build-settings-doc,boefjes,boefjes.config,boefjes,Boefjes)
	$(call build-settings-doc,bytes,bytes.config,bytes,Bytes)
	$(call build-settings-doc,mula/scheduler,config.settings,mula,Mula)

	curl -sL -o - https://registry.npmjs.org/d3/-/d3-7.9.0.tgz | tar -Oxzf - package/dist/d3.min.js > docs/source/_static/d3.min.js
	curl -sL -o - https://registry.npmjs.org/mermaid/-/mermaid-11.3.0.tgz | tar -Oxzf - package/dist/mermaid.min.js > docs/source/_static/mermaid.min.js

	echo "f2094bbf6141b359722c4fe454eb6c4b0f0e42cc10cc7af921fc158fceb86539  docs/source/_static/d3.min.js" | $(CHECKSUM_CMD) --check || exit 1
	echo "0d2b6f2361e7e0ce466a6ed458e03daa5584b42ef6926c3beb62eb64670ca261  docs/source/_static/mermaid.min.js" | $(CHECKSUM_CMD) --check || exit 1

	PYTHONPATH=$(PYTHONPATH):boefjes/:bytes/:mula/:octopoes/ sphinx-build -b html --fail-on-warning docs/source docs/_build


upgraderequirements:
	@echo "Upgrading all required dependencies using uv..."
	files=$$(find . -name pyproject.toml -maxdepth 2); \
	for path in $$files; do \
		project_dir=$$(dirname $$path); \
		echo "Processing $$path..."; \
		uv lock --project $$project_dir --upgrade; \
		echo "New Lock file generated. Use \`make requirements\` to update requirements files..."
	done

requirements:
	@echo "Generating requirements.txt files for all projects using uv..."
	files=$$(find . -name pyproject.toml -maxdepth 2); \
	for path in $$files; do \
		project_dir=$$(dirname $$path); \
		echo "Processing $$path..."; \
		uv lock --project $$project_dir --check; \
		echo "Exporting main dependencies..."; \
		uv export --project $$project_dir --no-default-groups --format requirements-txt -o $$project_dir/requirements.txt; \
		if grep -q "\[dependency-groups\]" $$path && grep -q "dev =" $$path; then \
			echo "Exporting dev dependencies..."; \
			uv export --project $$project_dir --group dev --format requirements-txt -o $$project_dir/requirements-dev.txt; \
		else \
			echo "No dev group, skipping requirements-dev.txt..."; \
		fi; \
	done


================================================
FILE: README.rst
================================================
================
What is OpenKAT?
================

OpenKAT aims to monitor, record and analyze the status of information systems. The basic premise is that many of the major security incidents are caused by small errors and known vulnerabilities, and if you find them and resolve them in time your systems and infrastructure become a lot more secure.

OpenKAT scans, collects, analyzes and reports in an ongoing process:

.. image:: docs/source/about-openkat/img/flowopenkat.png
  :alt: flow of OpenKAT

OpenKAT scans networks, finds vulnerabilities and creates accessible reports. It integrates the most widely used network tools and scanning software into a modular framework, accesses external databases such as shodan, and combines the information from all these sources into clear reports. It also includes lots of cat hair.

OpenKAT is useful if you want to monitor a complex system and know whether it contains known vulnerabilities or configuration errors. Due to its modular structure and extensibility, OpenKAT can be applied in different situations. You can customize it and put it to your own use.

Documentation
=============

`The full documentation of OpenKAT can be found here: https://docs.openkat.nl <https://docs.openkat.nl>`_. It includes information such as:

- Introduction to the system
- Modules
- Guidelines
- Templates
- Technical documentation
- Our `Figma / UX designs <https://docs.openkat.nl/ux_design/figma.html>`_.

Brochures
=========

The high level documentation on OpenKAT explains the purpose and operation of OpenKAT at the management level:

- `the 'TL;DR' of 2 pages (English) <https://github.com/minvws/nl-kat-coordination/blob/main/docs/source/about-openkat/pdf/OpenKAT%20handout_ENG.pdf>`_
- `the extensive brochure on OpenKAT (Dutch) <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/docs/source/about-openkat/pdf/introductie%20OpenKAT%20V20220621.pdf>`_

Current release
===============

The current release of OpenKAT can be found via the `release page on this repository <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/releases>`_.

Translations
============
.. image:: https://hosted.weblate.org/widget/openkat/287x66-white.png
   :target: https://hosted.weblate.org/engage/openkat/
   :alt: Translation status (summary)

.. image:: https://hosted.weblate.org/widget/openkat/multi-auto.svg
   :target: https://hosted.weblate.org/engage/openkat/
   :alt: Translation status (bar chart)

We gratefully use `Weblate <https://hosted.weblate.org/engage/openkat/>`_ to manage the translations.
See `the docs <https://docs.openkat.nl/guidelines/contributions.html#contribute-translations>`_ for more information.


Which license applies to OpenKAT?
=================================

OpenKAT is available under the `EU PL 1.2 license <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>`_. This license was chosen because it provides a reasonable degree of freedom while ensuring public character. The EU PL 1.2 license is retained upon further distribution of the software. Modifications and additions can be made under the EU PL 1.2 license or under compatible licenses, which are similar in nature.

The tools addressed by OpenKAT may have their own license, from the OS/S domain or from commercial application. This is the responsibility of the owner of the system addressing these tools. The inclusion of new boefjes in the KAT catalog is governed by a separate agreement.

Contact
=======

There are several options to contact the OpenKAT team:

- Direct contact: meedoen@openkat.nl
- `Github Discussions <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/discussions>`_
- `OpenKAT group on Linkedin <https://www.linkedin.com/>`_ (search for OpenKAT)
- IRC: #openkat on irc.libera.chat
- `Signal group <https://signal.group/#CjQKIIS4T1mDK1RcTqelkv-vDvnzrsU4b2qGj3xIPPrqWO8HEhDISi92dF_m4g7tXEB_QwN_>`_

Privacy
=======

OpenKAT is not designed to collect private information and it does not act on any private information that it finds. Some information considered to be personally identifiable information, may be collected through one or more of OpenKAT's plugins and subsequently stored, but only if that information has been accessible to OpenKAT. For example, a phone number or email address listed on a website might end up being collected as part of OpenKAT normal data collection. These data might then be stored for a long period of time, because OpenKAT stores evidence of its actions. No email or phone number models are present and as such they won't be processed into objects by OpenKAT.
An OpenKAT installation requires user accounts for users to be able to log in. These accounts (and all data OpenKAT works with) are stored only on the OpenKAT installation itself, and are not shared with any other parties or outside of your OpenKAT install.

Security
========

OpenKAT is designed to be secure by default in its production setup. In the development setup some debugging flags are enabled by default and it will not include TLS out of the box. To set up a secure production OpenKAT install, please follow the `Production setup guidelines <https://docs.openkat.nl/installation-and-deployment/install.html#production-environments>`_ and `Hardening guidelines <https://docs.openkat.nl/installation-and-deployment/hardening.html>`_.


================================================
FILE: boefjes/.ci/docker-compose.yml
================================================
services:
  katalogus_integration:
    build:
      context: ..
      dockerfile: boefjes/Dockerfile
      args:
        - ENVIRONMENT=dev
    command: sh -c 'python -m pytest -v tests/integration'
    depends_on:
      - ci_katalogus-db
      - ci_katalogus
    env_file:
      - .ci/.env.test
    volumes:
      - .:/app/boefjes

  ci_katalogus-db:
    image: docker.io/library/postgres:15
    env_file:
      - .ci/.env.test

  migration_bench:
    build:
      context: ..
      dockerfile: boefjes/Dockerfile
      args:
        - ENVIRONMENT=dev
    command: bash -c "python -m cProfile -o .ci/bench_$(date +%Y_%m_%d-%H:%M:%S).pstat -m pytest -v -m slow tests/integration/test_bench.py::test_migration"
    depends_on:
      - ci_bytes
      - ci_octopoes
      - ci_katalogus-db
    env_file:
      - .ci/.env.test
    volumes:
      - .:/app/boefjes
    environment:
      - DATABASE_MIGRATION=1

  ci_bytes:
    build:
      context: ../bytes
      args:
        ENVIRONMENT: dev
    command: uvicorn bytes.api:app --host 0.0.0.0
    depends_on:
      ci_rabbitmq:
        condition: service_healthy
      ci_bytes-db:
        condition: service_started
    env_file:
      - .ci/.env.test
    environment:
      - DATABASE_MIGRATION=1

  ci_bytes-db:
    image: docker.io/library/postgres:15
    env_file:
      - .ci/.env.test

  ci_octopoes:
    build:
      context: ../octopoes
    command: uvicorn octopoes.api.api:app --host 0.0.0.0 --port 80
    depends_on:
      ci_rabbitmq:
        condition: service_healthy
      ci_xtdb:
        condition: service_started
      ci_katalogus:
        condition: service_started
      ci_octopoes_api_worker:
        condition: service_started
    env_file:
      - .ci/.env.test

  ci_rabbitmq:
    restart: on-failure
    image: "docker.io/library/rabbitmq:3.12-management"
    healthcheck:
      test: ["CMD", "rabbitmqctl", "status"]
      interval: 5s
      retries: 4
    env_file:
      - .ci/.env.test

  ci_xtdb:
    image: "docker.underdark.nl/librekat/xtdb-http-multinode:main"

  ci_octopoes_api_worker:
    build:
      context: ../octopoes
    command: celery -A octopoes.tasks.tasks worker -E --loglevel=INFO
    depends_on:
      ci_rabbitmq:
        condition: service_healthy
      ci_xtdb:
        condition: service_started
    env_file:
      - .ci/.env.test
    ulimits:
      nofile:
        soft: 262144
        hard: 262144

  ci_katalogus:
    build:
      context: ..
      dockerfile: boefjes/Dockerfile
      args:
        - ENVIRONMENT=dev
    command: uvicorn boefjes.katalogus.root:app --host 0.0.0.0 --port 8080
    depends_on:
      - ci_katalogus-db
    env_file:
      - .ci/.env.test
    volumes:
      - .:/app/boefjes


================================================
FILE: boefjes/.dockerignore
================================================
**/__pycache__
**/*.pyc
**/*.pyo
**/*.pyd
.Python
env
.venv
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.log
.git
**/.mypy_cache
.pytest_cache
.hypothesis
Dockerfile
bytes-data
.dockerignore
.github
.env-dist
.gitignore
.ci
Makefile
packaging
README.*
pyproject.toml
sql_migrations
.dockerignore
export_migrations
docs


================================================
FILE: boefjes/.editorconfig
================================================
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.py]
indent_size = 4
max_line_length = 120

[Makefile]
indent_style = tab

[*.md]
trim_trailing_whitespace = false


================================================
FILE: boefjes/.gitignore
================================================

# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode
# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,node,react,intellij,vscode

### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/
.vscode

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn.  Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
*.iml
*.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721

# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr

# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/

# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml

# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/

# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$

# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml

### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test
.env*.local
*.env

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
pytestdebug.log

# Translations
*.mo
*.pot

# Django stuff:
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/
doc/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
pythonenv*

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# profiling data
.prof

### react ###
.DS_*
**/*.backup.*
**/*.back.*

node_modules

*.sublime*

psd
thumb
sketch

### vscode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace

### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db

# Dump file
*.stackdump

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp

# Windows shortcuts
*.lnk

# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode

/bytes/bytes-data
/errors
*.tar.gz
.password-store
.envrc
deployment/github_config.sh
**cache.db
*.swp

!/tests/test_spf.py

!packaging/deb/data/usr/lib

# debian build artifacts
debhelper-build-stamp
*.debhelper
*.deb
*.dsc
*.build
*.buildinfo
*.changes
*.substvars
debian/*/
debian/*.log
debian/files
debian/changelog


================================================
FILE: boefjes/Dockerfile
================================================
ARG PYTHON_VERSION=3.13
FROM python:$PYTHON_VERSION AS dev

ARG USER_UID=1000
ARG USER_GID=1000

ENTRYPOINT ["/app/boefjes/entrypoint.sh"]

RUN groupadd --gid "$USER_GID" nonroot
RUN adduser --disabled-password --gecos '' --uid "$USER_UID" --gid "$USER_GID" nonroot

WORKDIR /app/boefjes
ENV PATH=/home/nonroot/.local/bin:${PATH}

ARG ENVIRONMENT

COPY boefjes/requirements-dev.txt boefjes/requirements.txt .

RUN --mount=type=cache,target=/root/.cache \
    pip install --upgrade pip \
    && if [ "$ENVIRONMENT" = "dev" ]; \
    then \
    pip install -r requirements-dev.txt; \
    else \
    pip install -r requirements.txt; \
    fi

FROM dev

COPY octopoes/ /tmp/octopoes
RUN cd /tmp/octopoes && python setup.py bdist_wheel
RUN pip install /tmp/octopoes/dist/octopoes*.whl

COPY boefjes/entrypoint.sh .
COPY boefjes/boefjes ./boefjes

# FIXME: We currently have to run as root to be able to start containers using
# the docker socket
#USER nonroot

CMD ["python", "-m", "bin.worker", "boefje"]


================================================
FILE: boefjes/LICENSE
================================================
                      EUROPEAN UNION PUBLIC LICENCE v. 1.2
                      EUPL © the European Union 2007, 2016

This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
below) which is provided under the terms of this Licence. Any use of the Work,
other than as authorised under this Licence is prohibited (to the extent such
use is covered by a right of the copyright holder of the Work).

The Work is provided under the terms of this Licence when the Licensor (as
defined below) has placed the following notice immediately following the
copyright notice for the Work:

        Licensed under the EUPL

or has expressed by any other means his willingness to license under the EUPL.

1. Definitions

In this Licence, the following terms have the following meaning:

- ‘The Licence’: this Licence.

- ‘The Original Work’: the work or software distributed or communicated by the
  Licensor under this Licence, available as Source Code and also as Executable
  Code as the case may be.

- ‘Derivative Works’: the works or software that could be created by the
  Licensee, based upon the Original Work or modifications thereof. This Licence
  does not define the extent of modification or dependence on the Original Work
  required in order to classify a work as a Derivative Work; this extent is
  determined by copyright law applicable in the country mentioned in Article 15.

- ‘The Work’: the Original Work or its Derivative Works.

- ‘The Source Code’: the human-readable form of the Work which is the most
  convenient for people to study and modify.

- ‘The Executable Code’: any code which has generally been compiled and which is
  meant to be interpreted by a computer as a program.

- ‘The Licensor’: the natural or legal person that distributes or communicates
  the Work under the Licence.

- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
  Licence, or otherwise contributes to the creation of a Derivative Work.

- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
  the Work under the terms of the Licence.

- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
  renting, distributing, communicating, transmitting, or otherwise making
  available, online or offline, copies of the Work or providing access to its
  essential functionalities at the disposal of any other natural or legal
  person.

2. Scope of the rights granted by the Licence

The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
sublicensable licence to do the following, for the duration of copyright vested
in the Original Work:

- use the Work in any circumstance and for all usage,
- reproduce the Work,
- modify the Work, and make Derivative Works based upon the Work,
- communicate to the public, including the right to make available or display
  the Work or copies thereof to the public and perform publicly, as the case may
  be, the Work,
- distribute the Work or copies thereof,
- lend and rent the Work or copies thereof,
- sublicense rights in the Work or copies thereof.

Those rights can be exercised on any media, supports and formats, whether now
known or later invented, as far as the applicable law permits so.

In the countries where moral rights apply, the Licensor waives his right to
exercise his moral right to the extent allowed by law in order to make effective
the licence of the economic rights here above listed.

The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
any patents held by the Licensor, to the extent necessary to make use of the
rights granted on the Work under this Licence.

3. Communication of the Source Code

The Licensor may provide the Work either in its Source Code form, or as
Executable Code. If the Work is provided as Executable Code, the Licensor
provides in addition a machine-readable copy of the Source Code of the Work
along with each copy of the Work that the Licensor distributes or indicates, in
a notice following the copyright notice attached to the Work, a repository where
the Source Code is easily and freely accessible for as long as the Licensor
continues to distribute or communicate the Work.

4. Limitations on copyright

Nothing in this Licence is intended to deprive the Licensee of the benefits from
any exception or limitation to the exclusive rights of the rights owners in the
Work, of the exhaustion of those rights or of other applicable limitations
thereto.

5. Obligations of the Licensee

The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:

Attribution right: The Licensee shall keep intact all copyright, patent or
trademarks notices and all notices that refer to the Licence and to the
disclaimer of warranties. The Licensee must include a copy of such notices and a
copy of the Licence with every copy of the Work he/she distributes or
communicates. The Licensee must cause any Derivative Work to carry prominent
notices stating that the Work has been modified and the date of modification.

Copyleft clause: If the Licensee distributes or communicates copies of the
Original Works or Derivative Works, this Distribution or Communication will be
done under the terms of this Licence or of a later version of this Licence
unless the Original Work is expressly distributed only under this version of the
Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
(becoming Licensor) cannot offer or impose any additional terms or conditions on
the Work or Derivative Work that alter or restrict the terms of the Licence.

Compatibility clause: If the Licensee Distributes or Communicates Derivative
Works or copies thereof based upon both the Work and another work licensed under
a Compatible Licence, this Distribution or Communication can be done under the
terms of this Compatible Licence. For the sake of this clause, ‘Compatible
Licence’ refers to the licences listed in the appendix attached to this Licence.
Should the Licensee's obligations under the Compatible Licence conflict with
his/her obligations under this Licence, the obligations of the Compatible
Licence shall prevail.

Provision of Source Code: When distributing or communicating copies of the Work,
the Licensee will provide a machine-readable copy of the Source Code or indicate
a repository where this Source will be easily and freely available for as long
as the Licensee continues to distribute or communicate the Work.

Legal Protection: This Licence does not grant permission to use the trade names,
trademarks, service marks, or names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the copyright notice.

6. Chain of Authorship

The original Licensor warrants that the copyright in the Original Work granted
hereunder is owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.

Each Contributor warrants that the copyright in the modifications he/she brings
to the Work are owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.

Each time You accept the Licence, the original Licensor and subsequent
Contributors grant You a licence to their contributions to the Work, under the
terms of this Licence.

7. Disclaimer of Warranty

The Work is a work in progress, which is continuously improved by numerous
Contributors. It is not a finished work and may therefore contain defects or
‘bugs’ inherent to this type of development.

For the above reason, the Work is provided under the Licence on an ‘as is’ basis
and without warranties of any kind concerning the Work, including without
limitation merchantability, fitness for a particular purpose, absence of defects
or errors, accuracy, non-infringement of intellectual property rights other than
copyright as stated in Article 6 of this Licence.

This disclaimer of warranty is an essential part of the Licence and a condition
for the grant of any rights to the Work.

8. Disclaimer of Liability

Except in the cases of wilful misconduct or damages directly caused to natural
persons, the Licensor will in no event be liable for any direct or indirect,
material or moral, damages of any kind, arising out of the Licence or of the use
of the Work, including without limitation, damages for loss of goodwill, work
stoppage, computer failure or malfunction, loss of data or any commercial
damage, even if the Licensor has been advised of the possibility of such damage.
However, the Licensor will be liable under statutory product liability laws as
far such laws apply to the Work.

9. Additional agreements

While distributing the Work, You may choose to conclude an additional agreement,
defining obligations or services consistent with this Licence. However, if
accepting obligations, You may act only on your own behalf and on your sole
responsibility, not on behalf of the original Licensor or any other Contributor,
and only if You agree to indemnify, defend, and hold each Contributor harmless
for any liability incurred by, or claims asserted against such Contributor by
the fact You have accepted any warranty or additional liability.

10. Acceptance of the Licence

The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
placed under the bottom of a window displaying the text of this Licence or by
affirming consent in any other similar way, in accordance with the rules of
applicable law. Clicking on that icon indicates your clear and irrevocable
acceptance of this Licence and all of its terms and conditions.

Similarly, you irrevocably accept this Licence and all of its terms and
conditions by exercising any rights granted to You by Article 2 of this Licence,
such as the use of the Work, the creation by You of a Derivative Work or the
Distribution or Communication by You of the Work or copies thereof.

11. Information to the public

In case of any Distribution or Communication of the Work by means of electronic
communication by You (for example, by offering to download the Work from a
remote location) the distribution channel or media (for example, a website) must
at least provide to the public the information requested by the applicable law
regarding the Licensor, the Licence and the way it may be accessible, concluded,
stored and reproduced by the Licensee.

12. Termination of the Licence

The Licence and the rights granted hereunder will terminate automatically upon
any breach by the Licensee of the terms of the Licence.

Such a termination will not terminate the licences of any person who has
received the Work from the Licensee under the Licence, provided such persons
remain in full compliance with the Licence.

13. Miscellaneous

Without prejudice of Article 9 above, the Licence represents the complete
agreement between the Parties as to the Work.

If any provision of the Licence is invalid or unenforceable under applicable
law, this will not affect the validity or enforceability of the Licence as a
whole. Such provision will be construed or reformed so as necessary to make it
valid and enforceable.

The European Commission may publish other linguistic versions or new versions of
this Licence or updated versions of the Appendix, so far this is required and
reasonable, without reducing the scope of the rights granted by the Licence. New
versions of the Licence will be published with a unique version number.

All linguistic versions of this Licence, approved by the European Commission,
have identical value. Parties can take advantage of the linguistic version of
their choice.

14. Jurisdiction

Without prejudice to specific agreement between parties,

- any litigation resulting from the interpretation of this License, arising
  between the European Union institutions, bodies, offices or agencies, as a
  Licensor, and any Licensee, will be subject to the jurisdiction of the Court
  of Justice of the European Union, as laid down in article 272 of the Treaty on
  the Functioning of the European Union,

- any litigation arising between other parties and resulting from the
  interpretation of this License, will be subject to the exclusive jurisdiction
  of the competent court where the Licensor resides or conducts its primary
  business.

15. Applicable Law

Without prejudice to specific agreement between parties,

- this Licence shall be governed by the law of the European Union Member State
  where the Licensor has his seat, resides or has his registered office,

- this licence shall be governed by Belgian law if the Licensor has no seat,
  residence or registered office inside a European Union Member State.

Appendix

‘Compatible Licences’ according to Article 5 EUPL are:

- GNU General Public License (GPL) v. 2, v. 3
- GNU Affero General Public License (AGPL) v. 3
- Open Software License (OSL) v. 2.1, v. 3.0
- Eclipse Public License (EPL) v. 1.0
- CeCILL v. 2.0, v. 2.1
- Mozilla Public Licence (MPL) v. 2
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
  works other than software
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
  Reciprocity (LiLiQ-R+).

The European Commission may update this Appendix to later versions of the above
licences without producing a new version of the EUPL, as long as they provide
the rights granted in Article 2 of this Licence and protect the covered Source
Code from exclusive appropriation.

All other changes or additions to this Appendix require the production of a new
EUPL version.


================================================
FILE: boefjes/MANIFEST.in
================================================
include README.md
include LICENSE
include boefjes/alembic.ini

recursive-include boefjes/katalogus/static *
recursive-include boefjes/plugins *.json
recursive-include boefjes/plugins *.md
recursive-include boefjes/plugins *.png
recursive-include boefjes/plugins *.jpg
recursive-include boefjes/plugins *.txt


================================================
FILE: boefjes/Makefile
================================================
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# Makefile Reference: https://tech.davis-hansson.com/p/make/

.PHONY: help sql migrate migrations debian ubuntu clean images

# use HIDE to run commands invisibly, unless VERBOSE defined
HIDE:=$(if $(VERBOSE),,@)

export m		# Message for alembic migration
export revid	# Revision id to generate raw sql for
export rev1		# Previous revision id for generating migrations
export rev2		# New revision id for the new migration file

# We set this to build images with the right target platform in the `images` target. This prevents arm systems such as
# systems with an Apple silicon chip to build images they cannot use.
export DOCKER_DEFAULT_PLATFORM=$(shell docker system info --format '{{.OSType}}/{{.Architecture}}')

##
##|------------------------------------------------------------------------|
##			Help
##|------------------------------------------------------------------------|
help: ## Show this help.
	@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/:\(.*\)##/:			/' | sed -e 's/##//'


##
##|------------------------------------------------------------------------|
##			Development
##|------------------------------------------------------------------------|

build: images

base-image:
	docker build -f **/base.Dockerfile -t openkat/boefje-base:latest .

export REGISTRY=docker.underdark.nl/librekat

# TODO: nikto cannot run as a worker as it uses a custom javascript image. We should differentiate between these images.
# Build the images for the containerized boefjes
images: dns-sec nmap export-http nikto adr-validator masscan nuclei ssl-certificates ssl-scan testssl-sh-ciphers webpage-capture wp-scan pdio-subfinder generic

dns-sec: base-image
	docker build -f */*/kat_dnssec/boefje.Dockerfile -t $(REGISTRY)/openkat-dns-sec:latest -t openkat/dns-sec .

nmap: base-image
	docker build -f */*/kat_nmap_tcp/boefje.Dockerfile -t $(REGISTRY)/openkat-nmap:latest -t openkat/nmap .

export-http: base-image
	docker build -f */*/kat_export_http/boefje.Dockerfile -t $(REGISTRY)/openkat-export-http:latest -t openkat/export-http .

nikto: base-image
	docker build -f */*/kat_nikto/boefje.Dockerfile -t $(REGISTRY)/openkat-nikto:latest .

adr-validator: base-image
	docker build -f */*/kat_adr_validator/boefje.Dockerfile -t $(REGISTRY)/openkat-adr-validator:latest .

masscan: base-image
	docker build -f */*/kat_masscan/boefje.Dockerfile -t $(REGISTRY)/openkat-masscan:latest .

nuclei: base-image
	docker build -f */*/kat_nuclei_cve/boefje.Dockerfile -t $(REGISTRY)/openkat-nuclei:latest .

ssl-certificates: base-image
	docker build -f */*/kat_ssl_certificates/boefje.Dockerfile -t $(REGISTRY)/openkat-ssl-certificates:latest .

ssl-scan: base-image
	docker build -f */*/kat_ssl_scan/boefje.Dockerfile -t $(REGISTRY)/openkat-ssl-scan:latest .

testssl-sh-ciphers: base-image
	docker build -f */*/kat_testssl_sh_ciphers/boefje.Dockerfile -t $(REGISTRY)/openkat-testssl-sh-ciphers:latest .

webpage-capture: base-image
	docker build -f */*/kat_webpage_capture/boefje.Dockerfile -t $(REGISTRY)/openkat-webpage-capture:latest .

wp-scan: base-image
	docker build -f */*/kat_wpscan/boefje.Dockerfile -t $(REGISTRY)/openkat-wp-scan:latest .

pdio-subfinder: base-image
	docker build -f */*/kat_pdio_subfinder/boefje.Dockerfile -t $(REGISTRY)/openkat-pdio-subfinder:latest .

generic: base-image
	docker build -f */generic.Dockerfile -t $(REGISTRY)/openkat-generic:latest -t openkat/generic .


##
##|------------------------------------------------------------------------|
##			Migrations
##|------------------------------------------------------------------------|

migrations: ## Generate a migration using alembic
ifeq ($(m),)
	$(HIDE) (echo "Specify a message with m={message} and a rev-id with revid={revid} (e.g. 0001 etc.)"; exit 1)
else
	docker compose run --rm katalogus python -m alembic --config /app/boefjes/boefjes/alembic.ini revision --autogenerate -m "$(m)"
endif


sql: ## Generate raw sql for the migrations
	docker compose run --rm katalogus python -m alembic --config /app/boefjes/boefjes/alembic.ini upgrade $(rev1):$(rev2) --sql

check:
	pre-commit run --all-files --color always

##
##|------------------------------------------------------------------------|
##			Tests
##|------------------------------------------------------------------------|

ci-docker-compose := docker compose --project-directory . -f .ci/docker-compose.yml


test: itest ## Run all tests.

itest: ## Run the integration tests.
	$(ci-docker-compose) build
	$(ci-docker-compose) down --remove-orphans
	$(ci-docker-compose) run --rm katalogus_integration
	$(ci-docker-compose) stop

migration_bench:  ## Run the migration benchmark.
	$(ci-docker-compose) build
	$(ci-docker-compose) down --remove-orphans
	$(ci-docker-compose) run --rm migration_bench
	$(ci-docker-compose) stop

bench:  ## Run the other benchmarks
	$(ci-docker-compose) build
	$(ci-docker-compose) down --remove-orphans
	$(ci-docker-compose) run --rm katalogus_integration \
	python -m cProfile -o .ci/bench_$$(date +%Y_%m_%d-%H:%M:%S).pstat -m pytest -m slow --no-cov tests/integration
	$(ci-docker-compose) stop

debian12:
	docker run --rm \
	--env PKG_NAME=kat-boefjes \
	--env BUILD_DIR=./build \
	--env REPOSITORY=minvws/nl-kat-boefjes \
	--env RELEASE_VERSION=${RELEASE_VERSION} \
	--env RELEASE_TAG=${RELEASE_TAG} \
	--mount type=bind,src=${CURDIR},dst=/app \
	--mount type=bind,src=${CURDIR}/../octopoes,dst=/octopoes \
	--workdir /app \
	kat-debian12-build-image \
	packaging/scripts/build-debian-package.sh

ubuntu22.04:
	mkdir -p build
	docker run --rm \
	--env PKG_NAME=kat-boefjes \
	--env BUILD_DIR=./build \
	--env REPOSITORY=minvws/nl-kat-boefjes \
	--env RELEASE_VERSION=${RELEASE_VERSION} \
	--env RELEASE_TAG=${RELEASE_TAG} \
	--mount type=bind,src=${CURDIR},dst=/app \
	--mount type=bind,src=${CURDIR}/../octopoes,dst=/octopoes \
	--workdir /app \
	kat-ubuntu22.04-build-image \
	packaging/scripts/build-debian-package.sh

clean:
	rm -rf build
	rm -rf debian/kat-*/ debian/.debhelper debian/files *.egg-info/ dist/
	rm -f debian/debhelper-build-stamp
	rm -f debian/*.*.debhelper
	rm -f debian/*.substvars
	rm -f debian/*.debhelper.log
	rm -f debian/changelog


================================================
FILE: boefjes/boefjes/__init__.py
================================================


================================================
FILE: boefjes/boefjes/__main__.py
================================================
import click
import structlog
from sqlalchemy.orm import sessionmaker

from boefjes.clients.scheduler_client import SchedulerAPIClient
from boefjes.config import Settings, settings
from boefjes.dependencies.plugins import PluginService
from boefjes.job_handler import DockerBoefjeHandler, LocalNormalizerHandler, bytes_api_client
from boefjes.local.runner import LocalNormalizerJobRunner
from boefjes.logging import configure_logging
from boefjes.sql.config_storage import create_config_storage
from boefjes.sql.db import get_engine
from boefjes.sql.plugin_storage import create_plugin_storage
from boefjes.worker.boefje_handler import LocalBoefjeHandler
from boefjes.worker.interfaces import WorkerManager
from boefjes.worker.manager import SchedulerWorkerManager
from boefjes.worker.repository import get_local_repository

configure_logging()

logger = structlog.get_logger(__name__)


def get_runtime_manager(
    settings: Settings, queue: WorkerManager.Queue, images: list[str] | None = None, plugins: list[str] | None = None
) -> WorkerManager:
    local_repository = get_local_repository()

    session = sessionmaker(bind=get_engine())()
    plugin_service = PluginService(create_plugin_storage(session), create_config_storage(session), local_repository)
    scheduler_client = SchedulerAPIClient(plugin_service, str(settings.scheduler_api), images, plugins)

    item_handler: LocalBoefjeHandler | LocalNormalizerHandler | DockerBoefjeHandler

    if queue is WorkerManager.Queue.BOEFJES:
        item_handler = DockerBoefjeHandler(scheduler_client, bytes_api_client)
    else:
        item_handler = LocalNormalizerHandler(
            LocalNormalizerJobRunner(local_repository), bytes_api_client, settings.scan_profile_whitelist
        )

    return SchedulerWorkerManager(
        item_handler,
        scheduler_client,
        settings.pool_size,
        settings.poll_interval,
        settings.worker_heartbeat,
        settings.deduplicate,
    )


@click.command()
@click.argument("queue", type=click.Choice([q.value for q in WorkerManager.Queue]))
@click.option("--worker/--no-worker", "-w/-n", default=True, help="Whether to start a worker.")
@click.option("-i", "--images", type=str, default=None, multiple=True, help="A list of OCI images to filter on.")
@click.option("-p", "--plugins", type=str, default=None, multiple=True, help="A list of plugin ids to filter on.")
@click.option("--log-level", type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR"]), help="Log level", default="INFO")
def cli(queue: str, worker: bool, images: tuple[str] | None, plugins: tuple[str] | None, log_level: str) -> None:
    logger.setLevel(log_level)
    logger.info("Starting runtime for %s [image_filter=%s, plugin_filter=%s]", queue, images, plugins)

    if not plugins:
        parsed_plugins = settings.plugins or None
    else:
        parsed_plugins = list(plugins)

    if not images:
        parsed_images = settings.images or None
    else:
        parsed_images = list(images)

    runtime = get_runtime_manager(settings, WorkerManager.Queue(queue), parsed_images, parsed_plugins)

    if queue == "boefje":
        import boefjes.api

        boefjes.api.run()

        if worker:
            runtime.run(WorkerManager.Queue(queue))
    else:
        runtime.run(WorkerManager.Queue(queue))


if __name__ == "__main__":
    cli()


================================================
FILE: boefjes/boefjes/alembic.ini
================================================
# A generic, single database configuration.

[alembic]
# path to migration scripts
script_location = boefjes/migrations

# template used to generate migration files
file_template = %%(rev)s_%%(slug)s

# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .

# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python-dateutil library that can be
# installed by adding `alembic[tz]` to the pip requirements
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =

# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false

# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false

# version location specification; This defaults
# to migrations/versions.  When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "version_path_separator" below.
# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions

# version path separator; As mentioned above, this is the character used to split
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
# Valid values for version_path_separator are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
version_path_separator = os  # Use os.pathsep. Default configuration used for new projects.

# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8

sqlalchemy.url =


[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts.  See the documentation for further
# detail and examples

# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME

# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S


================================================
FILE: boefjes/boefjes/api.py
================================================
import multiprocessing
import uuid
from datetime import datetime, timezone
from multiprocessing.context import ForkContext, ForkProcess
from typing import Any
from uuid import UUID

import structlog
from fastapi import Body, Depends, FastAPI, HTTPException
from httpx import HTTPError, HTTPStatusError
from pydantic import BaseModel
from uvicorn import Config, Server

from boefjes.clients.bytes_client import BytesAPIClient
from boefjes.clients.scheduler_client import SchedulerAPIClient
from boefjes.config import settings
from boefjes.dependencies.plugins import get_plugin_service
from boefjes.worker.interfaces import BoefjeInput, BoefjeOutput, StatusEnum, Task, TaskStatus, WorkerManager
from boefjes.worker.repository import _default_mime_types

app = FastAPI(title="Boefje API")
logger = structlog.get_logger(__name__)
ctx: ForkContext = multiprocessing.get_context("fork")


class UvicornServer(ForkProcess):
    def __init__(self, config: Config):
        super().__init__()
        self.server = Server(config=config)
        self.config = config

    def stop(self) -> None:
        self.terminate()

    def run(self, *args, **kwargs):
        self.server.run()


def run():
    config = Config(app, host=settings.api_host, port=settings.api_port)
    instance = UvicornServer(config=config)
    instance.start()
    return instance


# Model for partial updates, only allowing a status update
class TaskIn(BaseModel):
    status: TaskStatus


def get_scheduler_client(plugin_service=Depends(get_plugin_service)):
    return SchedulerAPIClient(plugin_service, str(settings.scheduler_api))


def get_bytes_client():
    return BytesAPIClient(str(settings.bytes_api), username=settings.bytes_username, password=settings.bytes_password)


@app.get("/healthz")
async def root():
    return "OK"


@app.get("/api/v0/tasks/{task_id}", response_model=BoefjeInput)
def boefje_input(task_id: UUID, scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client)) -> BoefjeInput:
    task = get_task_from_scheduler(task_id, scheduler_client)

    if task.status is not TaskStatus.RUNNING:
        raise HTTPException(status_code=403, detail="Task does not have status running")

    output_url = str(settings.api).rstrip("/") + f"/api/v0/tasks/{task_id}"
    return BoefjeInput(task=task, output_url=output_url)


@app.post("/api/v0/tasks/{task_id}")
def boefje_output(
    task_id: UUID,
    boefje_output: BoefjeOutput,
    scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client),
    bytes_client: BytesAPIClient = Depends(get_bytes_client),
) -> dict[str, uuid.UUID]:
    task = get_task_from_scheduler(task_id, scheduler_client)
    boefje_meta = task.data

    if task.status is not TaskStatus.RUNNING:
        raise HTTPException(status_code=403, detail="Task does not have status running")

    boefje_meta.started_at = task.modified_at
    boefje_meta.ended_at = datetime.now(timezone.utc)

    try:
        bytes_client.login()
        bytes_client.save_boefje_meta(boefje_meta)

        bytes_response = {}

        if boefje_output.files:
            mime_types = _default_mime_types(boefje_meta.boefje)
            for file in boefje_output.files:
                file.tags = set(mime_types.union(file.tags)) if file.tags else set(mime_types)

            # when supported, also save file.name to Bytes
            bytes_response = bytes_client.save_raws(task_id, boefje_output)
    except HTTPError:
        scheduler_client.patch_task(task_id, TaskStatus.FAILED)
        return {}

    if boefje_output.status == StatusEnum.COMPLETED:
        scheduler_client.patch_task(task_id, TaskStatus.COMPLETED)
    elif boefje_output.status == StatusEnum.FAILED:
        scheduler_client.patch_task(task_id, TaskStatus.FAILED)

    return bytes_response


def get_task_from_scheduler(task_id: UUID, scheduler_client: SchedulerAPIClient):
    try:
        task = scheduler_client.get_task(task_id)
    except HTTPError as e:
        if isinstance(e, HTTPStatusError) and e.response.status_code == 404:
            raise HTTPException(status_code=404, detail="Task not found")
        else:
            logger.exception("Failed to get task from scheduler")
            raise HTTPException(status_code=500, detail="Internal server error")
    return task


# The "scheduler proxy" endpoints


@app.post("/api/v0/scheduler/{queue_id}/pop", response_model=list[Task], tags=["scheduler"])
def pop_tasks(
    queue_id: str,
    limit: int = 1,
    filters: dict[str, Any] | None = Body(...),
    scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client),
) -> list[Task]:
    return scheduler_client.pop_items(WorkerManager.Queue(queue_id), filters, limit)


@app.post("/api/v0/scheduler/{queue_id}/push", tags=["scheduler"])
def push_item(
    queue_id: str, p_item: Task, scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client)
) -> None:
    return scheduler_client.push_item(p_item)


@app.patch("/api/v0/scheduler/tasks/{task_id}", tags=["scheduler"])
def patch_task(
    task_id: uuid.UUID, task: TaskIn, scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client)
) -> None:
    return scheduler_client.patch_task(task_id, task.status)


@app.get("/api/v0/scheduler/tasks/{task_id}", response_model=Task, tags=["scheduler"])
def get_task(task_id: uuid.UUID, scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client)) -> Task:
    return get_task_from_scheduler(task_id, scheduler_client)


================================================
FILE: boefjes/boefjes/clients/__init__.py
================================================


================================================
FILE: boefjes/boefjes/clients/bytes_client.py
================================================
import typing
import uuid
from base64 import b64encode
from collections.abc import Callable
from functools import wraps
from typing import Any

import structlog
from httpx import Client, HTTPStatusError, HTTPTransport, Response

from boefjes.config import settings
from boefjes.worker.interfaces import BoefjeOutput, BoefjeStorageInterface
from boefjes.worker.job_models import BoefjeMeta, NormalizerMeta, RawDataMeta

BYTES_API_CLIENT_VERSION = "0.3"
logger = structlog.get_logger(__name__)

ClientSessionMethod = Callable[..., Any]


def retry_with_login(function: ClientSessionMethod) -> ClientSessionMethod:
    @wraps(function)
    def wrapper(self, *args, **kwargs):
        try:
            return function(self, *args, **kwargs)
        except HTTPStatusError as error:
            if error.response.status_code != 401:
                raise

            self.login()
            return function(self, *args, **kwargs)

    return typing.cast(ClientSessionMethod, wrapper)


class BytesAPIClient(BoefjeStorageInterface):
    def __init__(self, base_url: str, username: str, password: str):
        self._session = Client(
            base_url=base_url,
            headers={"User-Agent": f"bytes-api-client/{BYTES_API_CLIENT_VERSION}"},
            transport=(HTTPTransport(retries=6)),
            timeout=settings.outgoing_request_timeout,
        )

        self.credentials = {"username": username, "password": password}
        self.headers: dict[str, str] = {}

    def login(self) -> None:
        self.headers = self._get_authentication_headers()

    @staticmethod
    def _verify_response(response: Response) -> None:
        try:
            response.raise_for_status()
        except HTTPStatusError as error:
            if error.response.status_code != 401 and error.response.status_code != 404:
                logger.error(response.text)
            else:
                logger.debug(response.text)
            raise

    def _get_authentication_headers(self) -> dict[str, str]:
        return {"Authorization": f"bearer {self._get_token()}"}

    def _get_token(self) -> str:
        response = self._session.post(
            "/token", data=self.credentials, headers={"content-type": "application/x-www-form-urlencoded"}
        )

        return str(response.json()["access_token"])

    @retry_with_login
    def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:
        response = self._session.post(
            "/bytes/boefje_meta", json=boefje_meta.model_dump(mode="json"), headers=self.headers
        )

        self._verify_response(response)

    @retry_with_login
    def get_boefje_meta(self, boefje_meta_id: str) -> BoefjeMeta:
        response = self._session.get(f"/bytes/boefje_meta/{boefje_meta_id}", headers=self.headers)
        self._verify_response(response)

        return BoefjeMeta.model_validate_json(response.content)

    @retry_with_login
    def save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:
        response = self._session.post(
            "/bytes/normalizer_meta", json=normalizer_meta.model_dump(mode="json"), headers=self.headers
        )

        self._verify_response(response)

    @retry_with_login
    def get_normalizer_meta(self, normalizer_meta_id: uuid.UUID) -> NormalizerMeta:
        response = self._session.get(f"/bytes/normalizer_meta/{normalizer_meta_id}", headers=self.headers)
        self._verify_response(response)

        return NormalizerMeta.model_validate_json(response.content)

    @retry_with_login
    def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOutput) -> dict[str, uuid.UUID]:
        self.save_boefje_meta(boefje_meta)

        if boefje_output.files:
            return self.save_raws(boefje_meta.id, boefje_output)

        return {}

    @retry_with_login
    def save_raws(self, boefje_meta_id: uuid.UUID, boefje_output: BoefjeOutput) -> dict[str, uuid.UUID]:
        response = self._session.post(
            "/bytes/raw",
            json=boefje_output.model_dump(mode="json"),
            headers=self.headers,
            params={"boefje_meta_id": str(boefje_meta_id)},
        )
        self._verify_response(response)

        return response.json()

    @retry_with_login
    def save_raw(self, boefje_meta_id: str, raw: str | bytes, mime_types: set[str]) -> uuid.UUID:
        file_name = "raw"  # The name provides a key for all ids returned, so this is arbitrary as we only upload 1 file

        response = self._session.post(
            "/bytes/raw",
            json={
                "files": [
                    {
                        "name": file_name,
                        "content": b64encode(raw if isinstance(raw, bytes) else raw.encode()).decode(),
                        "tags": list(mime_types),
                    }
                ]
            },
            headers=self.headers,
            params={"boefje_meta_id": str(boefje_meta_id)},
        )
        self._verify_response(response)

        return uuid.UUID(response.json()[file_name])

    @retry_with_login
    def get_raw(self, raw_data_id: str) -> bytes:
        response = self._session.get(f"/bytes/raw/{raw_data_id}", headers=self.headers)
        self._verify_response(response)

        return response.content

    @retry_with_login
    def get_raws(self, boefje_meta_id: str) -> BoefjeOutput:
        response = self._session.get("/bytes/raws", headers=self.headers, params={"boefje_meta_id": boefje_meta_id})
        self._verify_response(response)

        return BoefjeOutput.model_validate_json(response.content)

    @retry_with_login
    def get_raw_meta(self, raw_data_id: str) -> RawDataMeta:
        response = self._session.get(f"/bytes/raw/{raw_data_id}/meta", headers=self.headers)
        self._verify_response(response)

        return RawDataMeta.model_validate_json(response.content)


================================================
FILE: boefjes/boefjes/clients/scheduler_client.py
================================================
import datetime
import os
import uuid
from functools import cache
from typing import Any

import httpx
import structlog
from httpx import Client, HTTPError, HTTPTransport, Response
from jsonschema import ValidationError
from jsonschema.validators import validate
from pydantic import TypeAdapter

from boefjes.config import settings
from boefjes.dependencies.plugins import PluginService
from boefjes.worker.interfaces import SchedulerClientInterface, Task, TaskPop, TaskStatus, WorkerManager
from boefjes.worker.job_models import BoefjeMeta
from octopoes.connector.octopoes import OctopoesAPIConnector
from octopoes.models import Reference
from octopoes.models.exception import ObjectNotFoundException

logger = structlog.get_logger(__name__)


class SchedulerAPIClient(SchedulerClientInterface):
    def __init__(
        self,
        plugin_service: PluginService,
        base_url: str,
        oci_images: list[str] | None = None,
        plugins: list[str] | None = None,
    ):
        self._session = Client(
            base_url=base_url, transport=HTTPTransport(retries=6), timeout=settings.outgoing_request_timeout
        )
        self.plugin_service = plugin_service
        self.oci_images = oci_images
        self.plugins = plugins

    @staticmethod
    def _verify_response(response: Response) -> None:
        response.raise_for_status()

    def pop_items(
        self, queue: WorkerManager.Queue, filters: dict[str, list[dict[str, Any]]] | None = None, limit: int | None = 1
    ) -> list[Task]:
        if not filters:
            filters = {"filters": []}
        if self.oci_images:
            filters["filters"].append(
                {"column": "data", "field": "boefje__oci_image", "operator": "in", "value": self.oci_images}
            )
        if self.plugins:
            filters["filters"].append(
                {"column": "data", "field": "boefje__id", "operator": "in", "value": self.plugins}
            )

        if queue.value == "normalizer":
            response = self._session.post(
                "/schedulers/normalizer/pop",
                json=filters if filters["filters"] else None,
                params={"limit": limit} if limit else None,
            )
        else:
            response = self._session.post(
                "/schedulers/boefje/pop",
                json=filters if filters["filters"] else None,
                params={"limit": limit} if limit else None,
            )
        self._verify_response(response)

        page = TypeAdapter(TaskPop | None).validate_json(response.content)

        if page is None:
            return []

        results = []
        for task in page.results:
            if isinstance(task.data, BoefjeMeta):
                try:
                    task.data = self._hydrate_boefje_meta(task.data)
                except (ValidationError, ObjectNotFoundException):
                    self.patch_task(task.id, TaskStatus.FAILED)
                    continue
            results.append(task)

        return results

    def push_item(self, p_item: Task) -> None:
        if p_item.scheduler_id not in ["boefje", "normalizer"]:
            raise ValueError("Invalid scheduler id")

        response = self._session.post(f"/schedulers/{p_item.scheduler_id}/push", json=p_item.model_dump(mode="json"))
        self._verify_response(response)

    def patch_task(self, task_id: uuid.UUID, status: TaskStatus) -> None:
        response = self._session.patch(f"/tasks/{task_id}", json={"status": status.value})
        self._verify_response(response)

    def get_task(self, task_id: uuid.UUID, hydrate: bool = True) -> Task:
        response = self._session.get(f"/tasks/{task_id}")
        self._verify_response(response)

        task = Task.model_validate_json(response.content)

        if hydrate and isinstance(task.data, BoefjeMeta):
            task.data = self._hydrate_boefje_meta(task.data)

        return task

    def _hydrate_boefje_meta(self, boefje_meta: BoefjeMeta) -> BoefjeMeta:
        with self.plugin_service as service:
            plugin = service.by_plugin_id(boefje_meta.boefje.id, boefje_meta.organization)

        # The octopoes API connector is organization-specific, where the client is generic.
        octopoes_api_connector = get_octopoes_api_connector(boefje_meta.organization)
        input_ooi = boefje_meta.input_ooi
        boefje_meta.arguments = {"oci_image": plugin.oci_image, "oci_arguments": plugin.oci_arguments}
        boefje_meta.runnable_hash = plugin.runnable_hash

        if input_ooi:
            reference = Reference.from_str(input_ooi)
            try:
                ooi = octopoes_api_connector.get(reference, valid_time=datetime.datetime.now(datetime.timezone.utc))
                boefje_meta.arguments["input"] = ooi.serialize()
            except ObjectNotFoundException:
                logger.info(
                    "Can't run boefje because OOI does not exist anymore",
                    reference=reference,
                    boefje_id=boefje_meta.boefje.id,
                    ooi=boefje_meta.input_ooi,
                    task_id=boefje_meta.id,
                )
                raise
        try:
            boefje_meta.environment = get_environment_settings(boefje_meta, plugin.boefje_schema)
        except ValidationError:
            logger.exception("The boefje environment was not set correctly")
            raise

        return boefje_meta


@cache
def boefje_env_variables() -> dict:
    """
    Return all environment variables that start with BOEFJE_. The returned
    keys have the BOEFJE_ prefix removed.
    """

    boefje_variables = {}
    for key, value in os.environ.items():
        if key.startswith("BOEFJE_"):
            boefje_variables[key.removeprefix("BOEFJE_")] = value

    return boefje_variables


def get_system_env_settings_for_boefje(allowed_keys: list[str]) -> dict:
    return {key: value for key, value in boefje_env_variables().items() if key in allowed_keys}


def get_environment_settings(boefje_meta: BoefjeMeta, schema: dict | None = None) -> dict[str, str]:
    try:
        katalogus_api = str(settings.katalogus_api).rstrip("/")
        response = httpx.get(
            f"{katalogus_api}/v1/organisations/{boefje_meta.organization}/{boefje_meta.boefje.id}/settings", timeout=30
        )
        response.raise_for_status()
    except HTTPError:
        logger.exception("Error getting environment settings")
        raise

    allowed_keys = schema.get("properties", []) if schema else []
    new_env = get_system_env_settings_for_boefje(allowed_keys)

    settings_from_katalogus = response.json()

    for key, value in settings_from_katalogus.items():
        if key in allowed_keys:
            new_env[key] = value

    # The schema, besides dictating that a boefje cannot run if it is not matched, also provides an extra safeguard:
    # it is possible to inject code if arguments are passed that "escape" the call to a tool. Hence, we should enforce
    # the schema somewhere and make the schema as strict as possible.
    if schema is not None:
        validate(instance=new_env, schema=schema)

    return {key: str(value) for key, value in new_env.items()}


def get_octopoes_api_connector(org_code: str) -> OctopoesAPIConnector:
    return OctopoesAPIConnector(str(settings.octopoes_api), org_code)


================================================
FILE: boefjes/boefjes/config.py
================================================
import logging
import os
from enum import Enum
from pathlib import Path
from typing import Any, Literal

from pydantic import AnyHttpUrl, Field, FilePath, IPvAnyAddress, PostgresDsn, conint
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict
from pydantic_settings.sources import EnvSettingsSource

BASE_DIR: Path = Path(__file__).parent.resolve()

# Set base dir to something generic when compiling environment docs
if os.getenv("DOCS"):
    BASE_DIR = Path("../")


class EncryptionMiddleware(Enum):
    IDENTITY = "IDENTITY"
    NACL_SEALBOX = "NACL_SEALBOX"


class BackwardsCompatibleEnvSettings(EnvSettingsSource):
    backwards_compatibility_mapping = {
        "BOEFJES_BOEFJE_API_HOST": "BOEFJES_API_HOST",
        "BOEFJES_BOEFJE_API_PORT": "BOEFJES_API_PORT",
        "BOEFJE_API": "BOEFJES_API",
        "BOEFJE_DOCKER_NETWORK": "BOEFJES_DOCKER_NETWORK",
        "LOG_CFG": "BOEFJES_LOG_CFG",
        "ENCRYPTION_MIDDLEWARE": "BOEFJES_ENCRYPTION_MIDDLEWARE",
        "KATALOGUS_PRIVATE_KEY_B64": "BOEFJES_KATALOGUS_PRIVATE_KEY",
        "KATALOGUS_PUBLIC_KEY_B64": "BOEFJES_KATALOGUS_PUBLIC_KEY",
    }

    def __call__(self) -> dict[str, Any]:
        d: dict[str, Any] = {}
        env_vars = {k.lower(): v for k, v in os.environ.items()}
        env_prefix = self.settings_cls.model_config.get("env_prefix", "").lower()

        for old_name, new_name in self.backwards_compatibility_mapping.items():
            old_name, new_name = old_name.lower(), new_name.lower()

            # New variable not explicitly set through env,
            # ...but old variable has been explicitly set through env
            if new_name not in env_vars and old_name in env_vars:
                logging.warning("Deprecation: %s is deprecated, use %s instead", old_name.upper(), new_name.upper())
                d[new_name[len(env_prefix) :]] = env_vars[old_name]

        return d


class Settings(BaseSettings):
    log_cfg: FilePath = Field(BASE_DIR / "logging.json", description="Path to the logging configuration file")

    # Worker configuration
    pool_size: int = Field(2, description="Number of workers to run per queue")
    poll_interval: float = Field(10.0, description="Time to wait before polling for tasks when all queues are empty")
    worker_heartbeat: float = Field(1.0, description="Seconds to wait before checking the workers when queues are full")
    deduplicate: bool = Field(False, description="Whether to apply deduplication")
    plugins: list[str] 
Download .txt
gitextract_do4ph06x/

├── .dockerignore
├── .env-defaults
├── .env-dist
├── .env-prod
├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── .gitignore
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   ├── feature_request.md
│   │   └── user_story.md
│   ├── dependabot.yml
│   ├── pull_request_template.md
│   ├── scripts/
│   │   ├── commit_sign_push.sh
│   │   └── coverage_file_fixer.py
│   └── workflows/
│       ├── boefjes-tests.yml
│       ├── boefjes_container_image.yml
│       ├── boefjes_tests.yml
│       ├── build-debian-docker-image.yml
│       ├── build-rdo-package.yml
│       ├── build_docs_on_pr.yml
│       ├── bytes-tests.yml
│       ├── bytes_container_image.yml
│       ├── bytes_tests.yml
│       ├── check_requirements.yml
│       ├── codeql.yml
│       ├── containerized_boefjes.yml
│       ├── debian_package.yml
│       ├── deploy_docs.yml
│       ├── mula-tests.yml
│       ├── mula_container_image.yml
│       ├── mula_tests.yml
│       ├── octopoes-tests.yml
│       ├── octopoes_container_image.yml
│       ├── octopoes_rtest.yml
│       ├── octopoes_tests.yml
│       ├── pre_commit_checks.yml
│       ├── rocky-tests.yml
│       ├── rocky_container_image.yml
│       ├── rocky_makelang.yml
│       ├── rocky_tests.yml
│       ├── sonar-cloud.yml
│       └── test_debian_packages_on_ubuntu.yml
├── .gitignore
├── .gitpod.yml
├── .pre-commit-config.yaml
├── .stylelintrc.json
├── CONTRIBUTING.rst
├── LICENSE
├── Makefile
├── README.rst
├── boefjes/
│   ├── .ci/
│   │   └── docker-compose.yml
│   ├── .dockerignore
│   ├── .editorconfig
│   ├── .gitignore
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── boefjes/
│   │   ├── __init__.py
│   │   ├── __main__.py
│   │   ├── alembic.ini
│   │   ├── api.py
│   │   ├── clients/
│   │   │   ├── __init__.py
│   │   │   ├── bytes_client.py
│   │   │   └── scheduler_client.py
│   │   ├── config.py
│   │   ├── dependencies/
│   │   │   ├── __init__.py
│   │   │   ├── encryption.py
│   │   │   └── plugins.py
│   │   ├── job_handler.py
│   │   ├── katalogus/
│   │   │   ├── __init__.py
│   │   │   ├── configs.py
│   │   │   ├── organisations.py
│   │   │   ├── plugins.py
│   │   │   ├── root.py
│   │   │   ├── settings.py
│   │   │   └── version.py
│   │   ├── local/
│   │   │   ├── __init__.py
│   │   │   └── runner.py
│   │   ├── logging.json
│   │   ├── logging.py
│   │   ├── migrations/
│   │   │   ├── __init__.py
│   │   │   ├── env.py
│   │   │   ├── script.py.mako
│   │   │   └── versions/
│   │   │       ├── 0001_add_katalogus_models.py
│   │   │       ├── 0002_change_lengths_of_several_char_fields.py
│   │   │       ├── 0003_longer_plugin_ids.py
│   │   │       ├── 197672984df0_make_organisation_code_field_larger.py
│   │   │       ├── 5be152459a7b_introduce_schema_field_to_boefje_model.py
│   │   │       ├── 6f99834a4a5a_introduce_boefje_and_normalizer_models.py
│   │   │       ├── 7c88b9cd96aa_remove_the_repository_model.py
│   │   │       ├── 870fc302b852_remove_environment_keys_field.py
│   │   │       ├── 9f48560b0000_add_schedule_interval_fields.py
│   │   │       ├── __init__.py
│   │   │       ├── a2c8d54b0124_unique_plugin_names.py
│   │   │       ├── cd34fdfafdaf_json_settings_for_settings_table.py
│   │   │       ├── f9de6eb7824b_introduce_boefjeconfig_model.py
│   │   │       ├── fc0295b38184_add_run_on_field_to_boefje.py
│   │   │       └── fdeaea4481b8_add_deduplication_flag.py
│   │   ├── normalizer_interfaces.py
│   │   ├── normalizer_models.py
│   │   ├── plugins/
│   │   │   ├── __init__.py
│   │   │   ├── helpers.py
│   │   │   ├── kat_adr_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── adr_finding_types.json
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_adr_validator/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_answer_parser/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_binaryedge/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── containers/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── databases/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── description.md
│   │   │   │   ├── http_web/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── main.py
│   │   │   │   ├── message_queues/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── protocols/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── remote_desktop/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   ├── schema.json
│   │   │   │   ├── service_identification/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   └── services/
│   │   │   │       ├── __init__.py
│   │   │   │       ├── normalize.py
│   │   │   │       └── normalizer.json
│   │   │   ├── kat_burpsuite/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_calvin/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_censys/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_crt_sh/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_cve_2023_34039/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── keys/
│   │   │   │   │   ├── vrni-6.0.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.0.0_platform
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.0.0_proxy
│   │   │   │   │   ├── vrni-6.1.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.1.0_platform
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.1.0_proxy
│   │   │   │   │   ├── vrni-6.10.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.10.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.10.0_platform
│   │   │   │   │   ├── vrni-6.2.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.2.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.2.0_platform
│   │   │   │   │   ├── vrni-6.3.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.3.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.3.0_platform
│   │   │   │   │   ├── vrni-6.4.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.4.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.4.0_platform
│   │   │   │   │   ├── vrni-6.5.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.5.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.5.0_platform
│   │   │   │   │   ├── vrni-6.6.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.6.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.6.0_platform
│   │   │   │   │   ├── vrni-6.7.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.7.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.7.0_platform
│   │   │   │   │   ├── vrni-6.8.0/
│   │   │   │   │   │   ├── id_rsa_vnera_keypair_6.8.0_collector
│   │   │   │   │   │   └── id_rsa_vnera_keypair_6.8.0_platform
│   │   │   │   │   └── vrni-6.9.0/
│   │   │   │   │       ├── id_rsa_vnera_keypair_6.9.0_collector
│   │   │   │   │       └── id_rsa_vnera_keypair_6.9.0_platform
│   │   │   │   └── main.py
│   │   │   ├── kat_cve_2023_35078/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_cve_2024_6387/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_cve_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_cwe_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── cwec_v4.16.xml
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_dicom/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_dns/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_dns_version/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_dns_zone/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_dnssec/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_export_http/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   └── schema.json
│   │   │   ├── kat_external_db/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_fierce/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── fierce.py
│   │   │   │   ├── lists/
│   │   │   │   │   ├── 20000.txt
│   │   │   │   │   ├── 5000.txt
│   │   │   │   │   └── default.txt
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_finding_normalizer/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_green_hosting/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_kat_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── kat_finding_types.json
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_leakix/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_manual/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── csv/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   └── single_ooi/
│   │   │   │       ├── __init__.py
│   │   │   │       ├── normalize.py
│   │   │   │       └── normalizer.json
│   │   │   ├── kat_masscan/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_maxmind_geoip/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_nikto/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.js
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── oci_adapter.js
│   │   │   │   ├── package.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_nmap_ip_range/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   └── schema.json
│   │   │   ├── kat_nmap_ports/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   └── schema.json
│   │   │   ├── kat_nmap_tcp/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_nmap_udp/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   └── schema.json
│   │   │   ├── kat_nuclei_cve/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_nuclei_exposed_panels/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_nuclei_take_over/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_pdio_subfinder/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_rdns/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_report_data/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_retirejs_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_rpki/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_security_txt_downloader/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_service_banner/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── kat_shodan/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_shodan_internetdb/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   ├── requirements.txt
│   │   │   │   └── schema.json
│   │   │   ├── kat_snyk/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── check_version.py
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_snyk_finding_types/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_ssl_certificates/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_ssl_scan/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── kat_testssl_sh_ciphers/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   ├── boefjes-requirements.txt
│   │   │   │   ├── description.md
│   │   │   │   ├── main.py
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── schema.json
│   │   │   ├── kat_wappalyzer/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── data/
│   │   │   │   │   ├── categories.json
│   │   │   │   │   ├── groups.json
│   │   │   │   │   ├── technologies/
│   │   │   │   │   │   ├── _.json
│   │   │   │   │   │   ├── a.json
│   │   │   │   │   │   ├── b.json
│   │   │   │   │   │   ├── c.json
│   │   │   │   │   │   ├── d.json
│   │   │   │   │   │   ├── e.json
│   │   │   │   │   │   ├── f.json
│   │   │   │   │   │   ├── g.json
│   │   │   │   │   │   ├── h.json
│   │   │   │   │   │   ├── i.json
│   │   │   │   │   │   ├── j.json
│   │   │   │   │   │   ├── k.json
│   │   │   │   │   │   ├── l.json
│   │   │   │   │   │   ├── m.json
│   │   │   │   │   │   ├── n.json
│   │   │   │   │   │   ├── o.json
│   │   │   │   │   │   ├── p.json
│   │   │   │   │   │   ├── q.json
│   │   │   │   │   │   ├── r.json
│   │   │   │   │   │   ├── s.json
│   │   │   │   │   │   ├── t.json
│   │   │   │   │   │   ├── u.json
│   │   │   │   │   │   ├── v.json
│   │   │   │   │   │   ├── w.json
│   │   │   │   │   │   ├── x.json
│   │   │   │   │   │   ├── y.json
│   │   │   │   │   │   └── z.json
│   │   │   │   │   ├── update.sh
│   │   │   │   │   └── urls.txt
│   │   │   │   ├── normalize.py
│   │   │   │   ├── normalizer.json
│   │   │   │   └── utils.py
│   │   │   ├── kat_webpage_analysis/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── check_images/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── description.md
│   │   │   │   ├── find_images_in_html/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── har/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── httpx.py
│   │   │   │   │   └── requests.py
│   │   │   │   ├── headers/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── normalize.py
│   │   │   │   │   └── normalizer.json
│   │   │   │   ├── main.py
│   │   │   │   └── requirements.txt
│   │   │   ├── kat_webpage_capture/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.Dockerfile
│   │   │   │   ├── boefje.Dockerfile.dockerignore
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   └── kat_wpscan/
│   │   │       ├── __init__.py
│   │   │       ├── boefje.Dockerfile
│   │   │       ├── boefje.Dockerfile.dockerignore
│   │   │       ├── boefje.json
│   │   │       ├── description.md
│   │   │       ├── main.py
│   │   │       ├── normalize.py
│   │   │       ├── normalizer.json
│   │   │       └── schema.json
│   │   ├── seed.py
│   │   ├── sql/
│   │   │   ├── __init__.py
│   │   │   ├── config_storage.py
│   │   │   ├── db.py
│   │   │   ├── db_models.py
│   │   │   ├── organisation_storage.py
│   │   │   ├── plugin_storage.py
│   │   │   └── session.py
│   │   ├── storage/
│   │   │   ├── __init__.py
│   │   │   ├── interfaces.py
│   │   │   └── memory.py
│   │   ├── version.py
│   │   └── worker/
│   │       ├── __init__.py
│   │       ├── __main__.py
│   │       ├── boefje_handler.py
│   │       ├── client.py
│   │       ├── interfaces.py
│   │       ├── job_models.py
│   │       ├── manager.py
│   │       ├── models.py
│   │       ├── oci_adapter.py
│   │       └── repository.py
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── install
│   │   ├── kat-boefjes.kat-boefjes.service
│   │   ├── kat-boefjes.kat-katalogus.service
│   │   ├── kat-boefjes.kat-normalizers.service
│   │   ├── kat-boefjes.sysusers
│   │   ├── postinst
│   │   ├── rules
│   │   └── triggers
│   ├── entrypoint.sh
│   ├── export_migrations/
│   │   ├── 0001_add_katalogus_models.sql
│   │   ├── 0002_change_lengths_of_several_char_fields.sql
│   │   ├── 0003_longer_plugin_ids.sql
│   │   ├── 0004_197672984df0_make_organisation_code_field_larger.sql
│   │   ├── 0005_cd34fdfafdaf_json_settings_for_settings_table.sql
│   │   ├── 0006_7c88b9cd96aa_remove_the_repository_model.sql
│   │   ├── 0007_6f99834a4a5a_introduce_boefje_and_normalizer_models.sql
│   │   ├── 0008_f9de6eb7824b_introduce_boefjeconfig_model.sql
│   │   ├── 0009_5be152459a7b_introduce_schema_field_to_boefje_model.sql
│   │   ├── 0010_870fc302b852_remove_environment_keys_field.sql
│   │   ├── 0011_a2c8d54b0124_unique_plugin_names.sql
│   │   ├── 0012_9f48560b0000_add_schedule_interval_fields.sql
│   │   ├── 0013_fc0295b38184_add_run_on_field_to_boefje.sql
│   │   └── 0014_fdeaea4481b8_add_deduplication_flag.sql
│   ├── images/
│   │   ├── base.Dockerfile
│   │   ├── base.Dockerfile.dockerignore
│   │   ├── generic.Dockerfile
│   │   ├── generic.Dockerfile.dockerignore
│   │   └── requirements.txt
│   ├── packaging/
│   │   ├── deb/
│   │   │   ├── Makefile
│   │   │   └── data/
│   │   │       ├── etc/
│   │   │       │   └── kat/
│   │   │       │       ├── boefjes.conf
│   │   │       │       ├── boefjes.logging.json
│   │   │       │       └── katalogus.gunicorn.conf.py
│   │   │       └── usr/
│   │   │           └── bin/
│   │   │               └── update-katalogus-db
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── setup.py
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── examples/
│   │   │   ├── adr-validator-normalize.json
│   │   │   ├── answer-normalize.json
│   │   │   ├── body-normalize.json
│   │   │   ├── body-page-analysis-normalize.json
│   │   │   ├── bodyimage-normalize.json
│   │   │   ├── calvin-normalizer.json
│   │   │   ├── cat_image
│   │   │   ├── cve_2023_35078_not_vulnerable.html
│   │   │   ├── cve_2023_35078_vulnerable.html
│   │   │   ├── dns-normalize.json
│   │   │   ├── download_body
│   │   │   ├── download_headers.json
│   │   │   ├── download_image_headers.json
│   │   │   ├── download_page_analysis.raw
│   │   │   ├── external_db.json
│   │   │   ├── inputs/
│   │   │   │   ├── cve-result-with-cvss.json
│   │   │   │   ├── cve-result-with-cvss2.json
│   │   │   │   ├── cve-result-without-cvss.json
│   │   │   │   ├── dns-result-example.com-cnames.json
│   │   │   │   ├── dns-result-example.nl.json
│   │   │   │   ├── dns-result-mx-example.nl.json
│   │   │   │   ├── dns-result-www.example.nl.json
│   │   │   │   ├── dns-zone-result-sub.example.nl.txt
│   │   │   │   ├── dnssec-self-signed.txt
│   │   │   │   ├── dnssec-status-line-not-last-line.txt
│   │   │   │   ├── dnssec-unsigned.txt
│   │   │   │   ├── dnssec-valid.txt
│   │   │   │   ├── fierce-result-example.com.json
│   │   │   │   ├── headers-check-input.json
│   │   │   │   ├── security_txt_result_different_website.json
│   │   │   │   ├── security_txt_result_same_website.json
│   │   │   │   ├── snyk-result-findings.json
│   │   │   │   ├── snyk-result-no-findings.json
│   │   │   │   ├── testssl-sh-ciphered.json
│   │   │   │   └── testssl-sh-cipherless.json
│   │   │   ├── log4shell-job.json
│   │   │   ├── manual-csv.json
│   │   │   ├── manual-ooi.json
│   │   │   ├── raw/
│   │   │   │   ├── leakix-example.com-output.txt
│   │   │   │   ├── leakix-example.com.json
│   │   │   │   ├── leakix-hostname-strict.json
│   │   │   │   ├── nikto-apache.dvwa.cloud.json
│   │   │   │   ├── nikto-example.com.json
│   │   │   │   ├── nikto-non-existing.com.json
│   │   │   │   └── nmap_mispoes.xml
│   │   │   ├── rdns-example1.txt
│   │   │   ├── rdns-example2.txt
│   │   │   ├── rdns-nxdomain.txt
│   │   │   ├── report-data-normalize.json
│   │   │   ├── report-data.json
│   │   │   ├── scheduler/
│   │   │   │   ├── pop_response_boefje.json
│   │   │   │   ├── pop_response_boefje_2.json
│   │   │   │   ├── pop_response_boefje_no_ooi.json
│   │   │   │   ├── pop_response_duplicated_boefje.json
│   │   │   │   ├── pop_response_duplicated_boefje_error.json
│   │   │   │   ├── pop_response_duplicated_docker_boefje.json
│   │   │   │   ├── pop_response_normalizer.json
│   │   │   │   ├── should_crash.json
│   │   │   │   └── should_crash_2.json
│   │   │   ├── snyk-job.json
│   │   │   ├── snyk-normalizer.json
│   │   │   ├── snyk-vuln.html
│   │   │   ├── snyk-vuln2.html
│   │   │   ├── snyk-vuln3.html
│   │   │   ├── ssl-certificates.txt
│   │   │   ├── user-changed.json
│   │   │   ├── user-login-admin-failure.json
│   │   │   ├── user-login-failure.json
│   │   │   └── webpage-analysis.json
│   │   ├── integration/
│   │   │   ├── __init__.py
│   │   │   ├── test_api.py
│   │   │   ├── test_bench.py
│   │   │   ├── test_get_environment.py
│   │   │   ├── test_json_settings_encryption_migration.py
│   │   │   ├── test_migration_add_schema_field.py
│   │   │   ├── test_remove_repository_migration.py
│   │   │   ├── test_settings_to_boefje_config_migration.py
│   │   │   └── test_sql_repositories.py
│   │   ├── katalogus/
│   │   │   ├── __init__.py
│   │   │   ├── test_organisation_api.py
│   │   │   ├── test_plugin_service.py
│   │   │   ├── test_plugins_api.py
│   │   │   └── test_settings.py
│   │   ├── loading.py
│   │   ├── modules/
│   │   │   ├── __init__.py
│   │   │   ├── dummy_bad_normalizer_dict_structure/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── dummy_bad_normalizer_return_type/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── dummy_boefje/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_boefje_environment/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_boefje_environment_with_pycache/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   ├── main.py
│   │   │   │   └── some_subdir/
│   │   │   │       └── __init__.py
│   │   │   ├── dummy_boefje_invalid_signature/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_boefje_missing_run/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_boefje_runtime_exception/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.json
│   │   │   │   └── main.py
│   │   │   ├── dummy_normalizer/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── dummy_normalizer_import_error/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── normalize.py
│   │   │   │   └── normalizer.json
│   │   │   ├── dummy_oci_boefje_no_main/
│   │   │   │   ├── __init__.py
│   │   │   │   └── boefje.json
│   │   │   └── kat_test/
│   │   │       ├── __init__.py
│   │   │       ├── boefje.json
│   │   │       ├── kat_test_2/
│   │   │       │   ├── __init__.py
│   │   │       │   ├── boefje.json
│   │   │       │   ├── kat_test_3/
│   │   │       │   │   ├── __init__.py
│   │   │       │   │   ├── normalize.py
│   │   │       │   │   └── normalizer.json
│   │   │       │   ├── main.py
│   │   │       │   └── schema.json
│   │   │       ├── kat_test_4/
│   │   │       │   ├── __init__.py
│   │   │       │   ├── boefje.json
│   │   │       │   └── schema.json
│   │   │       ├── main.py
│   │   │       ├── normalize.py
│   │   │       ├── normalizer.json
│   │   │       └── schema.json
│   │   ├── plugins/
│   │   │   ├── __init__.py
│   │   │   ├── test_adr_validator.py
│   │   │   ├── test_answer_parser.py
│   │   │   ├── test_bodyimage.py
│   │   │   ├── test_calvin.py
│   │   │   ├── test_cve-2023-35078.py
│   │   │   ├── test_cve-2024-6387.py
│   │   │   ├── test_cve_finding_types.py
│   │   │   ├── test_dns.py
│   │   │   ├── test_dnssec.py
│   │   │   ├── test_fierce.py
│   │   │   ├── test_generic_finding_normalizer.py
│   │   │   ├── test_leakix.py
│   │   │   ├── test_manual.py
│   │   │   ├── test_nmap.py
│   │   │   ├── test_rdns.py
│   │   │   ├── test_report_data.py
│   │   │   ├── test_scan_profiles.py
│   │   │   ├── test_security_txt.py
│   │   │   ├── test_snyk.py
│   │   │   ├── test_sslcertificate_normalizer.py
│   │   │   ├── test_testssl_sh.py
│   │   │   └── test_wappalyzer_normalizer.py
│   │   ├── test_api.py
│   │   ├── test_app.py
│   │   ├── test_job_handler.py
│   │   ├── test_models.py
│   │   ├── test_nikto_normalizer.py
│   │   └── test_tasks.py
│   └── tools/
│       ├── __init__.py
│       ├── run_boefje.py
│       ├── run_normalizer.py
│       ├── show_raw.py
│       └── upgrade_v1_17_0.py
├── bytes/
│   ├── .ci/
│   │   ├── docker-compose.yml
│   │   └── rabbitmq.conf
│   ├── .dockerignore
│   ├── .gitignore
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── bytes/
│   │   ├── __init__.py
│   │   ├── alembic.ini
│   │   ├── api/
│   │   │   ├── __init__.py
│   │   │   ├── metrics.py
│   │   │   ├── models.py
│   │   │   ├── root.py
│   │   │   └── router.py
│   │   ├── auth.py
│   │   ├── config.py
│   │   ├── database/
│   │   │   ├── __init__.py
│   │   │   ├── db.py
│   │   │   ├── db_models.py
│   │   │   ├── migrations/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── env.py
│   │   │   │   ├── script.py.mako
│   │   │   │   └── versions/
│   │   │   │       ├── 0001_initial.py
│   │   │   │       ├── 0002_drop_output_add_input.py
│   │   │   │       ├── 0003_rename_module_to_normalizer_boefje.py
│   │   │   │       ├── 0004_rename_boefje_name_to_boefje_id.py
│   │   │   │       ├── 0005_add_secure_hash_link_boefje_meta.py
│   │   │   │       ├── 0006_remove_redundant_dispatches_field.py
│   │   │   │       ├── 0007_add_raw_file_models.py
│   │   │   │       ├── 0008_point_normalizer_to_raw_file.py
│   │   │   │       ├── 0009_max_length_on_varchar_s.py
│   │   │   │       ├── 0010_longer_retrieval_link_string.py
│   │   │   │       ├── 0011_longer_normalizer_name_and_boefje_id.py
│   │   │   │       ├── 0011_rename_normalizer_name_to_normalizer_id.py
│   │   │   │       ├── 09a2929108d9_add_signing_provider_model.py
│   │   │   │       ├── 1dde0213e9fe_remove_all_hash_mime_types.py
│   │   │   │       ├── 65a39ab3e224_increase_organization_and_input_ooi_size.py
│   │   │   │       ├── __init__.py
│   │   │   │       ├── d216ad75177d_add_environment_and_runnable_hash_.py
│   │   │   │       ├── e09d8780e34b_nullable_input_ooi.py
│   │   │   │       ├── e2f76e95f1e7_.py
│   │   │   │       ├── ebc7de8be4e3_increase_organization_id_length.py
│   │   │   │       ├── ec68d3eb14b1_remove_redundant_boefje_meta_foreign.py
│   │   │   │       └── fa64454868a9_add_index_on_boefjemeta_table_.py
│   │   │   └── sql_meta_repository.py
│   │   ├── events/
│   │   │   ├── __init__.py
│   │   │   ├── events.py
│   │   │   └── manager.py
│   │   ├── models.py
│   │   ├── rabbitmq.py
│   │   ├── raw/
│   │   │   ├── __init__.py
│   │   │   ├── file_raw_repository.py
│   │   │   └── middleware.py
│   │   ├── repositories/
│   │   │   ├── __init__.py
│   │   │   ├── hash_repository.py
│   │   │   ├── meta_repository.py
│   │   │   └── raw_repository.py
│   │   ├── timestamping/
│   │   │   ├── __init__.py
│   │   │   ├── certificates/
│   │   │   │   └── freetsa.crt
│   │   │   ├── hashing.py
│   │   │   ├── in_memory.py
│   │   │   ├── pastebin.py
│   │   │   ├── provider.py
│   │   │   └── rfc3161.py
│   │   └── version.py
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── dirs
│   │   ├── install
│   │   ├── kat-bytes.service
│   │   ├── kat-bytes.sysusers
│   │   ├── postinst
│   │   ├── rules
│   │   └── triggers
│   ├── dev.logging.conf
│   ├── entrypoint.sh
│   ├── logging.conf
│   ├── packaging/
│   │   ├── deb/
│   │   │   ├── Makefile
│   │   │   └── data/
│   │   │       ├── etc/
│   │   │       │   └── kat/
│   │   │       │       ├── bytes.conf
│   │   │       │       ├── bytes.gunicorn.conf.py
│   │   │       │       └── bytes.logging.conf
│   │   │       └── usr/
│   │   │           └── bin/
│   │   │               └── update-bytes-db
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── setup.py
│   ├── sql_migrations/
│   │   ├── 0001_initial.sql
│   │   ├── 0002_drop_output_add_input.sql
│   │   ├── 0003_rename_module_to_normalizer_boefje.sql
│   │   ├── 0004_drop_dispatches_change_input_ooi.sql
│   │   ├── 0005_add_secure_hash_link_boefje_meta.sql
│   │   ├── 0006_remove_redundant_dispatches_field.sql
│   │   ├── 0007_add_raw_file_models.sql
│   │   ├── 0008_point_normalizer_to_raw.sql
│   │   ├── 0009_max_length_on_varchar_s.sql
│   │   ├── 0010_longer_retrieval_link_string.sql
│   │   ├── 0011_longer_normalizer_name_and_boefje_id.sql
│   │   ├── 0011_rename_normalizer_name_to_normalizer_id.sql
│   │   ├── 0012_65a39ab3e224_increate_organization_input_ooi_size.sql
│   │   ├── 0013_e09d8780e34b_nullable_input_ooi.sql
│   │   ├── 0014_ebc7de8be4e3_increase_organization_id_length.sql
│   │   ├── 0015_fa64454868a9_add_indices.sql
│   │   ├── 0016_ec68d3eb14b1_remove_redundant_boefje_meta_foreign.sql
│   │   ├── 0017_09a2929108d9_add_signing_provider_model.sql
│   │   ├── 0018_d216ad75177d_add_environment_and_runnable_hash.sql
│   │   └── 0019_1dde0213e9fe_remove_all_hash_mime_types.sql
│   └── tests/
│       ├── __init__.py
│       ├── client.py
│       ├── conftest.py
│       ├── integration/
│       │   ├── __init__.py
│       │   ├── requests/
│       │   │   └── calvin.http
│       │   ├── test_bytes_api.py
│       │   ├── test_event.py
│       │   ├── test_hash_service.py
│       │   ├── test_meta_repository.py
│       │   ├── test_migrations.py
│       │   └── test_timestamper.py
│       ├── loading.py
│       ├── seed_database.py
│       ├── stubs/
│       │   ├── dummy.json
│       │   ├── freetsa.crt
│       │   └── login-request.json
│       └── unit/
│           ├── __init__.py
│           ├── test_api.py
│           ├── test_auth.py
│           ├── test_context_mapping.py
│           ├── test_hash.py
│           └── test_raw_repository.py
├── cveapi/
│   ├── Dockerfile
│   ├── Makefile
│   ├── cveapi.py
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── kat-cveapi.service
│   │   ├── kat-cveapi.sysusers
│   │   ├── kat-cveapi.timer
│   │   └── rules
│   ├── entrypoint.sh
│   ├── packaging/
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   └── requirements.txt
├── docker-compose.release-example.yml
├── docker-compose.yml
├── docs/
│   ├── settings-doc-templates/
│   │   └── markdown.jinja
│   └── source/
│       ├── _static/
│       │   └── openkat.css
│       ├── about-openkat/
│       │   ├── index.rst
│       │   ├── release-notes/
│       │   │   ├── 1.10.rst
│       │   │   ├── 1.11.rst
│       │   │   ├── 1.12.rst
│       │   │   ├── 1.13.rst
│       │   │   ├── 1.14.rst
│       │   │   ├── 1.15.rst
│       │   │   ├── 1.16.rst
│       │   │   ├── 1.17.rst
│       │   │   ├── 1.18.rst
│       │   │   ├── 1.19.rst
│       │   │   ├── 1.20.rst
│       │   │   ├── 1.21.rst
│       │   │   ├── 1.5.rst
│       │   │   ├── 1.6.rst
│       │   │   ├── 1.7.rst
│       │   │   ├── 1.8.rst
│       │   │   ├── 1.9.rst
│       │   │   └── index.rst
│       │   └── what-is-openkat.rst
│       ├── conf.py
│       ├── developer-documentation/
│       │   ├── basic-principles/
│       │   │   ├── bits.rst
│       │   │   ├── boefjes-whiskers-bits.rst
│       │   │   ├── boefjes.rst
│       │   │   ├── index.rst
│       │   │   ├── introduction.rst
│       │   │   ├── modules.rst
│       │   │   ├── normalizers.rst
│       │   │   ├── origin-types.rst
│       │   │   ├── questions-and-configs.rst
│       │   │   └── verify-timestamps.rst
│       │   ├── boefjes-runner.md
│       │   ├── boefjes.md
│       │   ├── bytes.md
│       │   ├── contributor/
│       │   │   ├── guidelines/
│       │   │   │   ├── contributions.rst
│       │   │   │   ├── development.rst
│       │   │   │   ├── feature_flow.md
│       │   │   │   ├── ideas.rst
│       │   │   │   ├── index.rst
│       │   │   │   ├── management.rst
│       │   │   │   └── security.rst
│       │   │   ├── index.rst
│       │   │   ├── intro.rst
│       │   │   ├── templates/
│       │   │   │   ├── bug_report_template.md
│       │   │   │   ├── feature_request_template.md
│       │   │   │   ├── index.rst
│       │   │   │   ├── pull_request_template_author.md
│       │   │   │   ├── pull_request_template_review_code.md
│       │   │   │   └── pull_request_template_review_qa.md
│       │   │   └── ux-design/
│       │   │       ├── figma.rst
│       │   │       └── index.rst
│       │   ├── development-tutorial/
│       │   │   ├── creating-bit.md
│       │   │   ├── creating-boefje.rst
│       │   │   ├── creating-model.md
│       │   │   ├── creating-normalizer.md
│       │   │   ├── creating-report.md
│       │   │   ├── index.rst
│       │   │   └── testing-boefje.md
│       │   ├── index.rst
│       │   ├── mula.md
│       │   ├── normalisers-runner.md
│       │   ├── octopoes-models.rst
│       │   ├── octopoes-research.rst
│       │   ├── octopoes.md
│       │   ├── qa-test-plan.rst
│       │   ├── quick-start.rst
│       │   ├── reports.md
│       │   └── rocky.md
│       ├── index.rst
│       ├── installation-and-deployment/
│       │   ├── adding-proxy-to-openkat.rst
│       │   ├── cveapi.rst
│       │   ├── debugging-troubleshooting.rst
│       │   ├── developer-environment.rst
│       │   ├── environment-settings/
│       │   │   ├── index.rst
│       │   │   └── rocky.md
│       │   ├── events-and-logging.rst
│       │   ├── external-authentication.rst
│       │   ├── faq.rst
│       │   ├── gitpod.rst
│       │   ├── hardening.rst
│       │   ├── index.rst
│       │   ├── install.rst
│       │   ├── installation-options
│       │   ├── local-install.rst
│       │   ├── production-debian-environment.rst
│       │   ├── production-docker-environment.rst
│       │   ├── s3-buckets.rst
│       │   ├── scripts.rst
│       │   ├── separate-boefje-workers.rst
│       │   ├── users-and-organizations.rst
│       │   └── windows-install.rst
│       └── user-manual/
│           ├── basic-concepts/
│           │   ├── index.rst
│           │   ├── objects-and-recursion.rst
│           │   └── scan-levels-and-indemnification.rst
│           ├── getting-started/
│           │   ├── generate-report.rst
│           │   ├── index.rst
│           │   ├── introduction.rst
│           │   ├── login-and-registration.rst
│           │   ├── onboarding.rst
│           │   └── start-scanning.rst
│           ├── glossary.rst
│           ├── index.rst
│           └── navigation/
│               ├── crisis-room.rst
│               ├── findings.rst
│               ├── index.rst
│               ├── katalogus.rst
│               ├── members.rst
│               ├── objects.rst
│               ├── overview.rst
│               ├── reports.rst
│               ├── settings.rst
│               ├── tasks.rst
│               └── user-settings.rst
├── init-user-db.sh
├── monitoring/
│   ├── config.alloy
│   ├── grafana-provisioning/
│   │   ├── datasources/
│   │   │   └── pyroscope.yml
│   │   └── plugins/
│   │       └── explore-profiles.yml
│   ├── jaeger.yml
│   └── prometheus.yml
├── mula/
│   ├── .ci/
│   │   ├── docker-compose.yml
│   │   └── rabbitmq.conf
│   ├── .dockerignore
│   ├── .gitignore
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── install
│   │   ├── kat-mula.service
│   │   ├── kat-mula.sysusers
│   │   ├── postinst
│   │   ├── rules
│   │   └── triggers
│   ├── docs/
│   │   ├── api.md
│   │   ├── architecture.md
│   │   ├── configuration.md
│   │   ├── extending.md
│   │   └── metrics.md
│   ├── entrypoint.sh
│   ├── logging.json
│   ├── logging.prod.json
│   ├── packaging/
│   │   ├── deb/
│   │   │   ├── Makefile
│   │   │   └── data/
│   │   │       ├── etc/
│   │   │       │   └── kat/
│   │   │       │       ├── mula.conf
│   │   │       │       └── mula.logging.json
│   │   │       └── usr/
│   │   │           └── bin/
│   │   │               └── update-mula-db
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── scheduler/
│   │   ├── __init__.py
│   │   ├── __main__.py
│   │   ├── app.py
│   │   ├── clients/
│   │   │   ├── __init__.py
│   │   │   ├── amqp/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── listeners.py
│   │   │   │   ├── raw_data.py
│   │   │   │   └── scan_profile.py
│   │   │   ├── connector.py
│   │   │   ├── errors.py
│   │   │   └── http/
│   │   │       ├── __init__.py
│   │   │       ├── client.py
│   │   │       ├── external/
│   │   │       │   ├── __init__.py
│   │   │       │   ├── bytes.py
│   │   │       │   ├── katalogus.py
│   │   │       │   ├── octopoes.py
│   │   │       │   └── rocky.py
│   │   │       └── service.py
│   │   ├── config/
│   │   │   ├── __init__.py
│   │   │   └── settings.py
│   │   ├── context/
│   │   │   ├── __init__.py
│   │   │   └── context.py
│   │   ├── models/
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── boefje.py
│   │   │   ├── errors.py
│   │   │   ├── events.py
│   │   │   ├── health.py
│   │   │   ├── normalizer.py
│   │   │   ├── ooi.py
│   │   │   ├── organisation.py
│   │   │   ├── plugin.py
│   │   │   ├── queue.py
│   │   │   ├── raw_data.py
│   │   │   ├── schedule.py
│   │   │   ├── scheduler.py
│   │   │   └── task.py
│   │   ├── schedulers/
│   │   │   ├── __init__.py
│   │   │   ├── errors.py
│   │   │   ├── queue/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── errors.py
│   │   │   │   └── pq.py
│   │   │   ├── rankers/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── boefje.py
│   │   │   │   ├── normalizer.py
│   │   │   │   └── ranker.py
│   │   │   ├── scheduler.py
│   │   │   └── schedulers/
│   │   │       ├── __init__.py
│   │   │       ├── boefje.py
│   │   │       ├── normalizer.py
│   │   │       └── report.py
│   │   ├── server/
│   │   │   ├── __init__.py
│   │   │   ├── errors.py
│   │   │   ├── handlers/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── health.py
│   │   │   │   ├── metrics.py
│   │   │   │   ├── root.py
│   │   │   │   ├── schedulers.py
│   │   │   │   ├── schedules.py
│   │   │   │   └── tasks.py
│   │   │   ├── schemas/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── health.py
│   │   │   │   ├── schedule.py
│   │   │   │   ├── scheduler.py
│   │   │   │   └── task.py
│   │   │   ├── server.py
│   │   │   └── utils/
│   │   │       ├── __init__.py
│   │   │       └── pagination.py
│   │   ├── storage/
│   │   │   ├── __init__.py
│   │   │   ├── connection.py
│   │   │   ├── errors.py
│   │   │   ├── filters/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── casting.py
│   │   │   │   ├── comparison.py
│   │   │   │   ├── errors.py
│   │   │   │   ├── filters.py
│   │   │   │   ├── functions.py
│   │   │   │   └── operators.py
│   │   │   ├── migrations/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── alembic.ini
│   │   │   │   ├── env.py
│   │   │   │   ├── script.py.mako
│   │   │   │   └── versions/
│   │   │   │       ├── 0001_initial_migration.py
│   │   │   │       ├── 0002_update_tasks.py
│   │   │   │       ├── 0003_add_type_field_to_tasks.py
│   │   │   │       ├── 0004_add_server_default.py
│   │   │   │       ├── 0005_size_limit_hash.py
│   │   │   │       ├── 0006_add_jsonb_fields_add_jsonb_fields.py
│   │   │   │       ├── 0007_add_cancelled_status_for_tasks.py
│   │   │   │       ├── 0008_add_task_schedule.py
│   │   │   │       ├── 0009_add_organisation.py
│   │   │   │       ├── 0010_add_indices.py
│   │   │   │       ├── 0011_add_more_indexes.py
│   │   │   │       └── __init__.py
│   │   │   ├── stores/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── pq.py
│   │   │   │   ├── schedule.py
│   │   │   │   └── task.py
│   │   │   └── utils.py
│   │   ├── utils/
│   │   │   ├── __init__.py
│   │   │   ├── cron.py
│   │   │   ├── datastore.py
│   │   │   ├── dict_utils.py
│   │   │   ├── errors.py
│   │   │   ├── functions.py
│   │   │   └── thread.py
│   │   └── version.py
│   ├── scripts/
│   │   ├── .gitignore
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── __init__.py
│   │   ├── benchmark.py
│   │   ├── data.csv.example
│   │   └── load.py
│   ├── setup.py
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── factories/
│   │   │   ├── __init__.py
│   │   │   ├── boefje.py
│   │   │   ├── normalizer.py
│   │   │   ├── ooi.py
│   │   │   ├── organisation.py
│   │   │   ├── plugin.py
│   │   │   └── raw_data.py
│   │   ├── integration/
│   │   │   ├── __init__.py
│   │   │   ├── test_api.py
│   │   │   ├── test_app.py
│   │   │   ├── test_boefje_scheduler.py
│   │   │   ├── test_clients.py
│   │   │   ├── test_listeners.py
│   │   │   ├── test_normalizer_scheduler.py
│   │   │   ├── test_pq_store.py
│   │   │   ├── test_report_scheduler.py
│   │   │   ├── test_schedule_store.py
│   │   │   ├── test_scheduler.py
│   │   │   └── test_task_store.py
│   │   ├── mocks/
│   │   │   ├── __init__.py
│   │   │   ├── item.py
│   │   │   ├── listener.py
│   │   │   ├── queue.py
│   │   │   ├── ranker.py
│   │   │   ├── scheduler.py
│   │   │   ├── services.py
│   │   │   └── task.py
│   │   ├── simulation/
│   │   │   ├── __init__.py
│   │   │   └── test_simulation.py
│   │   ├── unit/
│   │   │   ├── __init__.py
│   │   │   ├── test_filter.py
│   │   │   ├── test_queue.py
│   │   │   ├── test_rankers.py
│   │   │   └── test_utils.py
│   │   └── utils/
│   │       ├── __init__.py
│   │       ├── functions.py
│   │       ├── json.py
│   │       └── memory.py
│   └── whitelist.py
├── octopoes/
│   ├── .ci/
│   │   ├── docker-compose.yml
│   │   ├── mock_bits/
│   │   │   └── url_classification_mock/
│   │   │       ├── __init__.py
│   │   │       ├── bit.py
│   │   │       └── url_classification_mock.py
│   │   └── wiremock/
│   │       └── mappings/
│   │           └── organisations.json
│   ├── .dockerignore
│   ├── .editorconfig
│   ├── .gitignore
│   ├── AUTHORS
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── bits/
│   │   ├── __init__.py
│   │   ├── ask_disallowed_domains/
│   │   │   ├── __init__.py
│   │   │   ├── ask_disallowed_domains.py
│   │   │   ├── bit.py
│   │   │   └── question_schema.json
│   │   ├── ask_port_specification/
│   │   │   ├── __init__.py
│   │   │   ├── ask_port_specification.py
│   │   │   ├── bit.py
│   │   │   └── question_schema.json
│   │   ├── ask_url_params_to_ignore/
│   │   │   ├── __init__.py
│   │   │   ├── ask_url_params_to_ignore.py
│   │   │   ├── bit.py
│   │   │   └── question_schema.json
│   │   ├── check_csp_header/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── check_csp_header.py
│   │   ├── check_cve_2021_41773/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── check_cve_2021_41773.py
│   │   ├── check_hsts_header/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── check_hsts_header.py
│   │   ├── cipher_classification/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   ├── cipher_classification.py
│   │   │   └── list-ciphers-openssl-with-finding-type.csv
│   │   ├── default_findingtype_risk/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── default_findingtype_risk.py
│   │   ├── definitions.py
│   │   ├── disallowed_csp_hostnames/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── disallowed_csp_hostnames.py
│   │   ├── dns_alias_resolving/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── dns_alias_resolving.py
│   │   ├── dns_resolving/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── dns_resolving.py
│   │   ├── domain_owner_verification/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── domain_owner_verification.py
│   │   ├── expiring_certificate/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── expiring_certificate.py
│   │   ├── https_availability/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── https_availability.py
│   │   ├── https_redirect/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── https_redirect.py
│   │   ├── internetnl/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── internetnl.py
│   │   ├── ipv6_nameservers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── ipv6_nameservers.py
│   │   ├── ipv6_webservers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── ipv6_webservers.py
│   │   ├── missing_caa/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_caa.py
│   │   ├── missing_dkim/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_dkim.py
│   │   ├── missing_dmarc/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_dmarc.py
│   │   ├── missing_headers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_headers.py
│   │   ├── missing_spf/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── missing_spf.py
│   │   ├── nxdomain_flag/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── nxdomain_flag.py
│   │   ├── nxdomain_header_flag/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── nxdomain_header_flag.py
│   │   ├── oois_in_headers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── oois_in_headers.py
│   │   ├── port_classification_ip/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── port_classification_ip.py
│   │   ├── port_common/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── port_common.py
│   │   ├── resource_discovery/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── resource_discovery.py
│   │   ├── retire_js/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   ├── retire_js.py
│   │   │   └── retirejs.json
│   │   ├── runner.py
│   │   ├── spf_discovery/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   ├── internetnl_spf_parser.py
│   │   │   └── spf_discovery.py
│   │   ├── ssl_certificate_hostname/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── ssl_certificate_hostname.py
│   │   ├── two_ipv6_nameservers/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── two_ipv6_nameservers.py
│   │   ├── url_classification/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── url_classification.py
│   │   ├── url_discovery/
│   │   │   ├── __init__.py
│   │   │   ├── bit.py
│   │   │   └── url_discovery.py
│   │   └── website_discovery/
│   │       ├── __init__.py
│   │       ├── bit.py
│   │       └── website_discovery.py
│   ├── conftest.py
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── install
│   │   ├── kat-octopoes.kat-octopoes-worker.service
│   │   ├── kat-octopoes.kat-octopoes.service
│   │   ├── kat-octopoes.sysusers
│   │   ├── postinst
│   │   ├── rules
│   │   └── triggers
│   ├── docs/
│   │   └── v3.md
│   ├── entrypoint.sh
│   ├── logging.yml
│   ├── octopoes/
│   │   ├── __init__.py
│   │   ├── api/
│   │   │   ├── __init__.py
│   │   │   ├── api.py
│   │   │   ├── bulk_router.py
│   │   │   ├── models.py
│   │   │   └── router.py
│   │   ├── config/
│   │   │   ├── __init__.py
│   │   │   ├── celery.py
│   │   │   └── settings.py
│   │   ├── connector/
│   │   │   ├── __init__.py
│   │   │   ├── katalogus.py
│   │   │   └── octopoes.py
│   │   ├── core/
│   │   │   ├── __init__.py
│   │   │   ├── app.py
│   │   │   └── service.py
│   │   ├── events/
│   │   │   ├── __init__.py
│   │   │   ├── events.py
│   │   │   └── manager.py
│   │   ├── models/
│   │   │   ├── __init__.py
│   │   │   ├── datetime.py
│   │   │   ├── exception.py
│   │   │   ├── explanation.py
│   │   │   ├── ooi/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── certificate.py
│   │   │   │   ├── config.py
│   │   │   │   ├── dns/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── records.py
│   │   │   │   │   └── zone.py
│   │   │   │   ├── email_security.py
│   │   │   │   ├── findings.py
│   │   │   │   ├── geography.py
│   │   │   │   ├── monitoring.py
│   │   │   │   ├── network.py
│   │   │   │   ├── question.py
│   │   │   │   ├── reports.py
│   │   │   │   ├── scans.py
│   │   │   │   ├── service.py
│   │   │   │   ├── software.py
│   │   │   │   └── web.py
│   │   │   ├── origin.py
│   │   │   ├── pagination.py
│   │   │   ├── path.py
│   │   │   ├── persistence.py
│   │   │   ├── transaction.py
│   │   │   ├── tree.py
│   │   │   └── types.py
│   │   ├── repositories/
│   │   │   ├── __init__.py
│   │   │   ├── ooi_repository.py
│   │   │   ├── origin_parameter_repository.py
│   │   │   ├── origin_repository.py
│   │   │   ├── repository.py
│   │   │   └── scan_profile_repository.py
│   │   ├── tasks/
│   │   │   ├── __init__.py
│   │   │   ├── app.py
│   │   │   ├── scanprofiles.py
│   │   │   └── tasks.py
│   │   ├── types.py
│   │   ├── version.py
│   │   └── xtdb/
│   │       ├── __init__.py
│   │       ├── client.py
│   │       ├── exceptions.py
│   │       ├── query.py
│   │       ├── query_builder.py
│   │       └── related_field_generator.py
│   ├── packaging/
│   │   ├── deb/
│   │   │   ├── Makefile
│   │   │   └── data/
│   │   │       └── etc/
│   │   │           └── kat/
│   │   │               ├── octopoes.conf
│   │   │               ├── octopoes.gunicorn.conf.py
│   │   │               └── octopoes.logging.yml
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── prod.logging.yml
│   ├── pyproject.toml
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── setup.py
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── fixtures/
│   │   │   ├── calvin_output_1.json
│   │   │   ├── calvin_output_2.json
│   │   │   ├── normalizer_output.json
│   │   │   ├── normalizer_output_config.json
│   │   │   ├── normalizer_output_empty.json
│   │   │   ├── normalizer_output_http.json
│   │   │   ├── normalizer_output_nxdomain.json
│   │   │   ├── regular_manual_declaration.json
│   │   │   └── regular_manual_declaration_with_null_values.json
│   │   ├── integration/
│   │   │   ├── __init__.py
│   │   │   ├── test_api_connector.py
│   │   │   ├── test_io.py
│   │   │   ├── test_ooi_deletion.py
│   │   │   ├── test_ooi_repository.py
│   │   │   ├── test_unicode.py
│   │   │   └── test_xtdb_client.py
│   │   ├── mocks/
│   │   │   ├── __init__.py
│   │   │   ├── mock_ooi_types.py
│   │   │   └── mock_ooi_types_abstract.py
│   │   ├── robot/
│   │   │   ├── 01_scan_profiles.robot
│   │   │   ├── 02_list_objects.robot
│   │   │   ├── 03_deletion_propagation.robot
│   │   │   ├── 04_save_declaration.robot
│   │   │   ├── 05_bits.robot
│   │   │   ├── 06_scan_profile_inheritance.robot
│   │   │   ├── 07_bit_configs.robot
│   │   │   ├── 07_rerun_bits.robot
│   │   │   ├── 08_findings.robot
│   │   │   ├── CeleryMonitor.py
│   │   │   ├── __init__.py
│   │   │   └── robot.resource
│   │   ├── test_api.py
│   │   ├── test_bit_ask_ports.py
│   │   ├── test_bit_cipher.py
│   │   ├── test_bit_csp_header.py
│   │   ├── test_bit_default_findingtype_risk.py
│   │   ├── test_bit_domain_verification.py
│   │   ├── test_bit_expiring_certificate.py
│   │   ├── test_bit_missing_headers.py
│   │   ├── test_bit_ports.py
│   │   ├── test_bit_spf_discovery.py
│   │   ├── test_bits.py
│   │   ├── test_disallowed_csp_hostnames.py
│   │   ├── test_event_manager.py
│   │   ├── test_octopoes_service.py
│   │   ├── test_ooi.py
│   │   ├── test_ooi_repository.py
│   │   ├── test_origin_repository.py
│   │   ├── test_path.py
│   │   ├── test_query.py
│   │   ├── test_query_builder_new.py
│   │   ├── test_recalculate_scan_profiles.py
│   │   ├── test_reference.py
│   │   ├── test_reference_node.py
│   │   ├── test_scan_profile_repository.py
│   │   └── test_type_analysis.py
│   └── tools/
│       ├── __init__.py
│       ├── analyze-bit-metric.py
│       ├── rename-origin-method.py
│       ├── run_bit.py
│       ├── xtdb-cli.py
│       └── xtdb_client.py
├── packaging/
│   ├── debian12/
│   │   └── Dockerfile
│   └── ubuntu22.04/
│       └── Dockerfile
├── pyproject.toml
├── requirements.txt
├── rfd/
│   ├── 0001-rfd.md
│   ├── 0002-code-of-conduct.md
│   ├── 0003-deduplication.md
│   ├── README.md
│   ├── assets/
│   │   └── .gitkeep
│   └── prototypes/
│       └── prototype.md
├── rocky/
│   ├── .ci/
│   │   └── docker-compose.yml
│   ├── .dockerignore
│   ├── .editorconfig
│   ├── .gitignore
│   ├── .nvmrc
│   ├── .parcelrc
│   ├── .prettierignore
│   ├── .sassrc
│   ├── Dockerfile
│   ├── LICENSE
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── OOI_database_seed.json
│   ├── account/
│   │   ├── __init__.py
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── forms/
│   │   │   ├── __init__.py
│   │   │   ├── account_setup.py
│   │   │   ├── login.py
│   │   │   ├── organization.py
│   │   │   ├── password_reset.py
│   │   │   └── token.py
│   │   ├── management/
│   │   │   ├── __init__.py
│   │   │   └── commands/
│   │   │       ├── __init__.py
│   │   │       └── create_authtoken.py
│   │   ├── migrations/
│   │   │   ├── 0001_initial.py
│   │   │   ├── 0001_squashed_0004_authtoken_authtoken_unique_name.py
│   │   │   ├── 0002_remove_first_last_name.py
│   │   │   ├── 0003_alter_katuser_full_name.py
│   │   │   ├── 0004_authtoken_authtoken_unique_name.py
│   │   │   ├── 0005_katuser_clearance_level.py
│   │   │   └── __init__.py
│   │   ├── mixins.py
│   │   ├── models.py
│   │   ├── templates/
│   │   │   ├── account_detail.html
│   │   │   ├── password_reset.html
│   │   │   ├── password_reset_confirm.html
│   │   │   ├── password_reset_done.html
│   │   │   ├── password_reset_email.html
│   │   │   ├── password_reset_subject.txt
│   │   │   ├── recover_email.html
│   │   │   ├── registration_email.html
│   │   │   └── registration_subject.txt
│   │   ├── urls.py
│   │   ├── validators.py
│   │   └── views/
│   │       ├── __init__.py
│   │       ├── account.py
│   │       ├── login.py
│   │       ├── password_reset.py
│   │       └── recover_email.py
│   ├── assets/
│   │   ├── css/
│   │   │   ├── abstracts/
│   │   │   │   ├── alert-colors.scss
│   │   │   │   └── mixins.scss
│   │   │   ├── components/
│   │   │   │   ├── action-buttons.scss
│   │   │   │   ├── block-indented.scss
│   │   │   │   ├── button-plain.scss
│   │   │   │   ├── cat-loader.scss
│   │   │   │   ├── cat-paw-loader.scss
│   │   │   │   ├── chapter-numbering.scss
│   │   │   │   ├── cytoscape.scss
│   │   │   │   ├── dashboard.scss
│   │   │   │   ├── destructive.scss
│   │   │   │   ├── dropdown-form.scss
│   │   │   │   ├── dropdown-list.scss
│   │   │   │   ├── dropdown.scss
│   │   │   │   ├── filter.scss
│   │   │   │   ├── footer-logo.scss
│   │   │   │   ├── footer.scss
│   │   │   │   ├── form-checkbox.scss
│   │   │   │   ├── form-inline.scss
│   │   │   │   ├── form.scss
│   │   │   │   ├── header-navigation.scss
│   │   │   │   ├── hover-block.scss
│   │   │   │   ├── input-link.scss
│   │   │   │   ├── introduction.scss
│   │   │   │   ├── language.scss
│   │   │   │   ├── layout-tiles-width-1.scss
│   │   │   │   ├── layout.scss
│   │   │   │   ├── messages.scss
│   │   │   │   ├── notifications.scss
│   │   │   │   ├── ooi-summary.scss
│   │   │   │   ├── page-meta.scss
│   │   │   │   ├── plugins.scss
│   │   │   │   ├── print.scss
│   │   │   │   ├── print_report.scss
│   │   │   │   ├── qr-code.scss
│   │   │   │   ├── report-name-table.scss
│   │   │   │   ├── report.scss
│   │   │   │   ├── risk-level-indicator.scss
│   │   │   │   ├── scan-level-indicator.scss
│   │   │   │   ├── select.scss
│   │   │   │   ├── state-tag.scss
│   │   │   │   ├── state-tags.scss
│   │   │   │   ├── stepper-variables.scss
│   │   │   │   ├── stepper.scss
│   │   │   │   ├── sticky-column.scss
│   │   │   │   ├── sticky.scss
│   │   │   │   ├── system-tag.scss
│   │   │   │   ├── table-sortable.scss
│   │   │   │   ├── table-state-icons.scss
│   │   │   │   ├── table.scss
│   │   │   │   ├── toggle.scss
│   │   │   │   ├── toolbar.scss
│   │   │   │   ├── tree-tables.scss
│   │   │   │   ├── user-icon.scss
│   │   │   │   └── wait-text.scss
│   │   │   ├── helpers/
│   │   │   │   ├── align-right.scss
│   │   │   │   ├── is-hidden.scss
│   │   │   │   └── uc-first.scss
│   │   │   ├── main.scss
│   │   │   ├── manon-components.scss
│   │   │   ├── report.scss
│   │   │   ├── themes/
│   │   │   │   └── soft/
│   │   │   │       ├── fonts/
│   │   │   │       │   ├── fonts.scss
│   │   │   │       │   ├── fredoka/
│   │   │   │       │   │   ├── OFL.txt
│   │   │   │       │   │   └── fredoka.scss
│   │   │   │       │   ├── kat-icons/
│   │   │   │       │   │   └── kat-icons.scss
│   │   │   │       │   ├── open-sans/
│   │   │   │       │   │   ├── OFL.txt
│   │   │   │       │   │   └── open-sans.scss
│   │   │   │       │   └── tabler-icons/
│   │   │   │       │       ├── LICENSE
│   │   │   │       │       └── tabler-icons.scss
│   │   │   │       ├── fundamentals/
│   │   │   │       │   ├── body-text-set.scss
│   │   │   │       │   ├── border-radii.scss
│   │   │   │       │   ├── colors.scss
│   │   │   │       │   ├── tag-colors.scss
│   │   │   │       │   └── tags-6-3.scss
│   │   │   │       ├── manon/
│   │   │   │       │   ├── accordion.scss
│   │   │   │       │   ├── application-base.scss
│   │   │   │       │   ├── article-content-wrapper.scss
│   │   │   │       │   ├── article.scss
│   │   │   │       │   ├── breadcrumb-bar.scss
│   │   │   │       │   ├── button-destructive.scss
│   │   │   │       │   ├── button-ghost.scss
│   │   │   │       │   ├── button-icon.scss
│   │   │   │       │   ├── button.scss
│   │   │   │       │   ├── checkbox.scss
│   │   │   │       │   ├── code-base.scss
│   │   │   │       │   ├── code-block.scss
│   │   │   │       │   ├── collapsible.scss
│   │   │   │       │   ├── collapsing-element.scss
│   │   │   │       │   ├── de-emphasized.scss
│   │   │   │       │   ├── description-list.scss
│   │   │   │       │   ├── emphasized.scss
│   │   │   │       │   ├── expando-rows.scss
│   │   │   │       │   ├── filter.scss
│   │   │   │       │   ├── footer.scss
│   │   │   │       │   ├── form-button.scss
│   │   │   │       │   ├── form-fieldset.scss
│   │   │   │       │   ├── form-help.scss
│   │   │   │       │   ├── form-inline.scss
│   │   │   │       │   ├── form-radio.scss
│   │   │   │       │   ├── form.scss
│   │   │   │       │   ├── header-navigation-collapsible.scss
│   │   │   │       │   ├── header-navigation-content-wrapper.scss
│   │   │   │       │   ├── header-navigation-link-active.scss
│   │   │   │       │   ├── header-navigation-link.scss
│   │   │   │       │   ├── header-navigation.scss
│   │   │   │       │   ├── headings-base-set.scss
│   │   │   │       │   ├── headings.scss
│   │   │   │       │   ├── hero.scss
│   │   │   │       │   ├── icon.scss
│   │   │   │       │   ├── image-square.scss
│   │   │   │       │   ├── language-selector-list.scss
│   │   │   │       │   ├── layout-column-4.scss
│   │   │   │       │   ├── layout-form.scss
│   │   │   │       │   ├── layout-set.scss
│   │   │   │       │   ├── layout.scss
│   │   │   │       │   ├── link.scss
│   │   │   │       │   ├── login-meta.scss
│   │   │   │       │   ├── logo.scss
│   │   │   │       │   ├── main.scss
│   │   │   │       │   ├── manon-variables.scss
│   │   │   │       │   ├── max-line-length.scss
│   │   │   │       │   ├── navigation-collapsible.scss
│   │   │   │       │   ├── navigation.scss
│   │   │   │       │   ├── nota-bene.scss
│   │   │   │       │   ├── notification-block.scss
│   │   │   │       │   ├── notification-colors.scss
│   │   │   │       │   ├── notification-paragraph.scss
│   │   │   │       │   ├── section-content-wrapper.scss
│   │   │   │       │   ├── section.scss
│   │   │   │       │   ├── sidemenu.scss
│   │   │   │       │   ├── spacing.scss
│   │   │   │       │   ├── spot-large.scss
│   │   │   │       │   ├── table.scss
│   │   │   │       │   ├── tabs.scss
│   │   │   │       │   ├── tags.scss
│   │   │   │       │   ├── text-colors.scss
│   │   │   │       │   └── tile.scss
│   │   │   │       ├── report.scss
│   │   │   │       └── soft.scss
│   │   │   └── vendor_overrides/
│   │   │       ├── graph-override.scss
│   │   │       ├── manon/
│   │   │       │   ├── button-icon-only.scss
│   │   │       │   ├── display-toggle.scss
│   │   │       │   ├── dl.scss
│   │   │       │   ├── form-fieldset-required.scss
│   │   │       │   ├── form-radio.scss
│   │   │       │   ├── image-square.scss
│   │   │       │   ├── layout-fifty-fifty.scss
│   │   │       │   ├── layout-form.scss
│   │   │       │   ├── nested-section.scss
│   │   │       │   ├── notification.scss
│   │   │       │   ├── table.scss
│   │   │       │   ├── tabs.scss
│   │   │       │   └── tile.scss
│   │   │       ├── two-factor.scss
│   │   │       └── weasyprint/
│   │   │           ├── accordion.scss
│   │   │           ├── application-base.scss
│   │   │           ├── button.scss
│   │   │           ├── chapter-numbers.scss
│   │   │           ├── description-list.scss
│   │   │           ├── headings.scss
│   │   │           ├── layout-centered.scss
│   │   │           ├── pdf-overrides.scss
│   │   │           ├── report-manon-components.scss
│   │   │           ├── section-wrapper.scss
│   │   │           ├── section.scss
│   │   │           ├── table-of-contents.scss
│   │   │           ├── table.scss
│   │   │           ├── tile.scss
│   │   │           ├── toolbar.scss
│   │   │           ├── ul.scss
│   │   │           └── visually-hidden.scss
│   │   ├── js/
│   │   │   ├── app.js
│   │   │   ├── autoSubmit.js
│   │   │   ├── checkboxToggler.js
│   │   │   ├── choiceToggle.js
│   │   │   ├── components.js
│   │   │   ├── dashboard.js
│   │   │   ├── dropdown.js
│   │   │   ├── enableButtonTimeout.js
│   │   │   ├── grabSelectionOnModalOpen.js
│   │   │   ├── imports/
│   │   │   │   ├── graph.js
│   │   │   │   ├── manon.js
│   │   │   │   └── utils.js
│   │   │   ├── jsonSchemaToForm.js
│   │   │   ├── pluginToggler.js
│   │   │   ├── renameReports.js
│   │   │   ├── report.js
│   │   │   ├── reportActionForms.js
│   │   │   ├── sidemenuOl.js
│   │   │   ├── stringHelpers.js
│   │   │   ├── tabs.js
│   │   │   ├── taskDetails.js
│   │   │   └── utils.js
│   │   └── vendors/
│   │       └── graph/
│   │           ├── css/
│   │           │   └── graph-d3.css
│   │           └── js/
│   │               ├── graph-d3.js
│   │               └── graph-render.js
│   ├── components/
│   │   ├── __init__.py
│   │   ├── main.scss
│   │   └── modal/
│   │       ├── README.md
│   │       ├── __init__.py
│   │       ├── modal.py
│   │       ├── script.js
│   │       ├── style.scss
│   │       └── template.html
│   ├── crisis_room/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── forms.py
│   │   ├── management/
│   │   │   ├── __init__.py
│   │   │   └── commands/
│   │   │       ├── __init__.py
│   │   │       ├── dashboards.py
│   │   │       └── recipe_seeder.json
│   │   ├── migrations/
│   │   │   ├── 0001_initial.py
│   │   │   ├── 0002_create_findings_dashboards.py
│   │   │   ├── 0003_alter_dashboarddata_unique_together_and_more.py
│   │   │   ├── 0004_change_name_findings_dashboard.py
│   │   │   ├── 0005_add_dashboard_permissions_to_groups.py
│   │   │   ├── 0006_rename_dashboarddata_dashboarditem.py
│   │   │   ├── 0007_alter_dashboarditem_source.py
│   │   │   └── __init__.py
│   │   ├── models.py
│   │   ├── templates/
│   │   │   ├── crisis_room.html
│   │   │   ├── crisis_room_dashboards.html
│   │   │   ├── crisis_room_findings.html
│   │   │   ├── crisis_room_header.html
│   │   │   ├── organization_crisis_room.html
│   │   │   ├── organization_crisis_room_dashboard_items.html
│   │   │   ├── organization_crisis_room_header.html
│   │   │   └── partials/
│   │   │       ├── crisis_room_findings_table.html
│   │   │       ├── dashboard_finding_list.html
│   │   │       ├── dashboard_finding_list_table.html
│   │   │       ├── dashboard_item_action_button.html
│   │   │       ├── dashboard_ooi_list.html
│   │   │       ├── dashboard_ooi_list_table.html
│   │   │       ├── delete_dashboard_item_modal.html
│   │   │       ├── delete_dashboard_modal.html
│   │   │       ├── new_dashboard_item_action_button.html
│   │   │       ├── new_dashboard_item_explanation_modal.html
│   │   │       ├── new_dashboard_item_modal.html
│   │   │       ├── new_dashboard_item_modal_report_section.html
│   │   │       ├── new_dashboard_modal.html
│   │   │       ├── report_dashboard_item_filter.html
│   │   │       └── report_section_action_button.html
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   └── crisis_room.py
│   │   ├── urls.py
│   │   └── views.py
│   ├── database.env-dist
│   ├── debian/
│   │   ├── control
│   │   ├── copyright
│   │   ├── install
│   │   ├── kat-rocky-worker.service
│   │   ├── kat-rocky.service
│   │   ├── kat-rocky.sysusers
│   │   ├── postinst
│   │   ├── postrm
│   │   ├── rules
│   │   └── triggers
│   ├── docs/
│   │   └── reports.md
│   ├── entrypoint.sh
│   ├── fmea/
│   │   ├── __init__.py
│   │   └── migrations/
│   │       ├── 0001_initial.py
│   │       ├── 0001_squashed_0004_remove_failuremode_effect_and_more.py
│   │       ├── 0002_auto_20220120_0839.py
│   │       ├── 0003_auto_20220203_1534.py
│   │       ├── 0004_remove_failuremode_effect_and_more.py
│   │       └── __init__.py
│   ├── katalogus/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── client.py
│   │   ├── exceptions.py
│   │   ├── forms/
│   │   │   ├── __init__.py
│   │   │   ├── katalogus_filter.py
│   │   │   └── plugin_settings.py
│   │   ├── health.py
│   │   ├── templates/
│   │   │   ├── about_plugins.html
│   │   │   ├── boefje_detail.html
│   │   │   ├── boefje_setup.html
│   │   │   ├── boefjes.html
│   │   │   ├── change_clearance_level.html
│   │   │   ├── clone_settings.html
│   │   │   ├── confirmation_clone_settings.html
│   │   │   ├── katalogus.html
│   │   │   ├── katalogus_settings.html
│   │   │   ├── normalizer_detail.html
│   │   │   ├── normalizers.html
│   │   │   ├── partials/
│   │   │   │   ├── boefje_tile.html
│   │   │   │   ├── enable_disable_plugin.html
│   │   │   │   ├── katalogus_filter.html
│   │   │   │   ├── katalogus_header.html
│   │   │   │   ├── katalogus_toolbar.html
│   │   │   │   ├── modal_report_types.html
│   │   │   │   ├── no_enabling_permission_message.html
│   │   │   │   ├── objects_to_scan.html
│   │   │   │   ├── plugin_settings_required.html
│   │   │   │   ├── plugin_tile.html
│   │   │   │   ├── plugin_tile_modal.html
│   │   │   │   ├── plugins.html
│   │   │   │   ├── plugins_navigation.html
│   │   │   │   ├── single_action_checkbox_form.html
│   │   │   │   └── single_action_form.html
│   │   │   ├── plugin_container_image.html
│   │   │   ├── plugin_settings_add.html
│   │   │   ├── plugin_settings_delete.html
│   │   │   └── plugin_settings_list.html
│   │   ├── urls.py
│   │   └── views/
│   │       ├── __init__.py
│   │       ├── boefje_setup.py
│   │       ├── change_clearance_level.py
│   │       ├── katalogus.py
│   │       ├── katalogus_settings.py
│   │       ├── mixins.py
│   │       ├── plugin_detail.py
│   │       ├── plugin_enable_disable.py
│   │       ├── plugin_settings_add.py
│   │       ├── plugin_settings_delete.py
│   │       ├── plugin_settings_edit.py
│   │       └── plugin_settings_list.py
│   ├── logging.json
│   ├── manage.py
│   ├── onboarding/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── forms.py
│   │   ├── migrations/
│   │   │   └── __init__.py
│   │   ├── templates/
│   │   │   ├── partials/
│   │   │   │   ├── cat_loader.html
│   │   │   │   ├── onboarding_header.html
│   │   │   │   ├── step_1_introduction_text.html
│   │   │   │   └── step_2_organization_text.html
│   │   │   ├── step_10_report.html
│   │   │   ├── step_1_introduction_registration.html
│   │   │   ├── step_1a_introduction.html
│   │   │   ├── step_2a_organization_setup.html
│   │   │   ├── step_2b_organization_update.html
│   │   │   ├── step_3_indemnification_setup.html
│   │   │   ├── step_4_trusted_acknowledge_clearance_level.html
│   │   │   ├── step_5_add_scan_ooi.html
│   │   │   ├── step_6_set_clearance_level.html
│   │   │   ├── step_7_clearance_level_introduction.html
│   │   │   ├── step_8_setup_scan_select_plugins.html
│   │   │   └── step_9_choose_report_type.html
│   │   ├── urls.py
│   │   ├── view_helpers.py
│   │   └── views.py
│   ├── package.json
│   ├── packaging/
│   │   ├── deb/
│   │   │   └── data/
│   │   │       ├── etc/
│   │   │       │   └── kat/
│   │   │       │       └── rocky.conf
│   │   │       └── usr/
│   │   │           └── bin/
│   │   │               └── rocky-cli
│   │   └── scripts/
│   │       └── build-debian-package.sh
│   ├── pyproject.toml
│   ├── reports/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── forms.py
│   │   ├── management/
│   │   │   ├── __init__.py
│   │   │   └── commands/
│   │   │       ├── __init__.py
│   │   │       └── worker.py
│   │   ├── migrations/
│   │   │   └── __init__.py
│   │   ├── report_types/
│   │   │   ├── __init__.py
│   │   │   ├── aggregate_organisation_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── appendix.html
│   │   │   │   ├── asset_overview.html
│   │   │   │   ├── basic_security.html
│   │   │   │   ├── findings.html
│   │   │   │   ├── recommendations.html
│   │   │   │   ├── report.html
│   │   │   │   ├── report.py
│   │   │   │   ├── rpki.html
│   │   │   │   ├── safe_connections.html
│   │   │   │   ├── summary.html
│   │   │   │   ├── system_specific.html
│   │   │   │   ├── system_specific_overview.html
│   │   │   │   ├── term_overview.html
│   │   │   │   └── vulnerabilities.html
│   │   │   ├── concatenated_report/
│   │   │   │   ├── __init__.py
│   │   │   │   └── report.py
│   │   │   ├── definitions.py
│   │   │   ├── dns_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── findings_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── helpers.py
│   │   │   ├── ipv6_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── mail_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── multi_organization_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── appendix.html
│   │   │   │   ├── asset_overview.html
│   │   │   │   ├── basic_security_details.html
│   │   │   │   ├── ipv6.html
│   │   │   │   ├── open_ports.html
│   │   │   │   ├── recommendations.html
│   │   │   │   ├── report.html
│   │   │   │   ├── report.py
│   │   │   │   ├── summary.html
│   │   │   │   └── vulnerabilities.html
│   │   │   ├── name_server_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── open_ports_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── rpki_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── safe_connections_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── systems_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── tls_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   ├── vulnerability_report/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── report.html
│   │   │   │   └── report.py
│   │   │   └── web_system_report/
│   │   │       ├── __init__.py
│   │   │       ├── report.html
│   │   │       └── report.py
│   │   ├── runner/
│   │   │   ├── __init__.py
│   │   │   ├── models.py
│   │   │   ├── report_runner.py
│   │   │   └── worker.py
│   │   ├── serializers.py
│   │   ├── templates/
│   │   │   ├── aggregate_report.html
│   │   │   ├── aggregate_report_pdf.html
│   │   │   ├── forms/
│   │   │   │   └── report_form_fields.html
│   │   │   ├── generate_report/
│   │   │   │   ├── export_setup.html
│   │   │   │   ├── select_oois.html
│   │   │   │   ├── select_report_types.html
│   │   │   │   └── setup_scan.html
│   │   │   ├── generate_report.html
│   │   │   ├── generate_report_pdf.html
│   │   │   ├── multi_report.html
│   │   │   ├── multi_report_pdf.html
│   │   │   ├── partials/
│   │   │   │   ├── export_report_settings.html
│   │   │   │   ├── generate_report_header.html
│   │   │   │   ├── generate_report_sidemenu.html
│   │   │   │   ├── new_report_action_button.html
│   │   │   │   ├── plugin_overview_table.html
│   │   │   │   ├── report_findings_table.html
│   │   │   │   ├── report_header.html
│   │   │   │   ├── report_introduction.html
│   │   │   │   ├── report_names_form.html
│   │   │   │   ├── report_ooi_list.html
│   │   │   │   ├── report_setup_scan.html
│   │   │   │   ├── report_severity_totals_table.html
│   │   │   │   ├── report_sidemenu.html
│   │   │   │   ├── report_types_selection.html
│   │   │   │   ├── report_types_tiles.html
│   │   │   │   ├── report_types_tiles_required_optional.html
│   │   │   │   └── return_button.html
│   │   │   ├── report_overview/
│   │   │   │   ├── modal_partials/
│   │   │   │   │   ├── delete_modal.html
│   │   │   │   │   ├── enable_disable_schedule_modal.html
│   │   │   │   │   ├── rename_modal.html
│   │   │   │   │   ├── rerun_modal.html
│   │   │   │   │   └── share_modal.html
│   │   │   │   ├── report_history.html
│   │   │   │   ├── report_history_table.html
│   │   │   │   ├── report_overview_header.html
│   │   │   │   ├── report_overview_navigation.html
│   │   │   │   ├── scheduled_reports.html
│   │   │   │   ├── scheduled_reports_table.html
│   │   │   │   ├── subreports.html
│   │   │   │   ├── subreports_header.html
│   │   │   │   └── subreports_table.html
│   │   │   ├── report_schedules/
│   │   │   │   └── delete_recipe_modal.html
│   │   │   └── summary/
│   │   │       ├── ooi_selection.html
│   │   │       ├── report_asset_overview.html
│   │   │       ├── selected_plugins.html
│   │   │       └── service_health.html
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   └── report_extra.py
│   │   ├── urls.py
│   │   ├── utils.py
│   │   ├── views/
│   │   │   ├── __init__.py
│   │   │   ├── aggregate_report.py
│   │   │   ├── base.py
│   │   │   ├── generate_report.py
│   │   │   ├── mixins.py
│   │   │   ├── multi_report.py
│   │   │   ├── report_overview.py
│   │   │   └── view_helpers.py
│   │   └── viewsets.py
│   ├── requirements-dev.txt
│   ├── requirements.txt
│   ├── rocky/
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── asgi.py
│   │   ├── auth/
│   │   │   ├── __init__.py
│   │   │   └── remote_user.py
│   │   ├── bytes_client.py
│   │   ├── exceptions.py
│   │   ├── forms.py
│   │   ├── health.py
│   │   ├── locale/
│   │   │   ├── de/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── django.pot
│   │   │   ├── en@pirate/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── es/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── fr/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── fy/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── it/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── nl/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   ├── pap/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── django.po
│   │   │   └── ta/
│   │   │       └── LC_MESSAGES/
│   │   │           └── django.po
│   │   ├── messaging.py
│   │   ├── middleware/
│   │   │   ├── __init__.py
│   │   │   ├── auth_required.py
│   │   │   ├── onboarding.py
│   │   │   ├── otel.py
│   │   │   └── remote_user.py
│   │   ├── otel.py
│   │   ├── paginator.py
│   │   ├── permissions.py
│   │   ├── scheduler.py
│   │   ├── settings.py
│   │   ├── settings_test.py
│   │   ├── signals.py
│   │   ├── templates/
│   │   │   ├── 403.html
│   │   │   ├── 404.html
│   │   │   ├── admin/
│   │   │   │   ├── base.html
│   │   │   │   ├── change_form.html
│   │   │   │   ├── change_list.html
│   │   │   │   ├── delete_confirmation.html
│   │   │   │   ├── delete_selected_confirmation.html
│   │   │   │   ├── popup_response.html
│   │   │   │   └── prepopulated_fields_js.html
│   │   │   ├── dashboard_client.html
│   │   │   ├── dashboard_redteam.html
│   │   │   ├── finding_type_add.html
│   │   │   ├── findings/
│   │   │   │   ├── finding_add.html
│   │   │   │   ├── finding_list.html
│   │   │   │   └── findings_filter.html
│   │   │   ├── footer.html
│   │   │   ├── forms/
│   │   │   │   ├── json_schema_form.html
│   │   │   │   └── widgets/
│   │   │   │       ├── checkbox_group_boefje_tiles.html
│   │   │   │       ├── checkbox_group_columns.html
│   │   │   │       ├── checkbox_group_table.html
│   │   │   │       ├── checkbox_option.html
│   │   │   │       ├── datalist.html
│   │   │   │       ├── input_option.html
│   │   │   │       └── widget_label.html
│   │   │   ├── graph-d3.html
│   │   │   ├── head.html
│   │   │   ├── header.html
│   │   │   ├── health.html
│   │   │   ├── indemnification_add.html
│   │   │   ├── indemnification_present.html
│   │   │   ├── landing_page.html
│   │   │   ├── layouts/
│   │   │   │   └── base.html
│   │   │   ├── legal/
│   │   │   │   └── privacy_statement.html
│   │   │   ├── oois/
│   │   │   │   ├── error.html
│   │   │   │   ├── ooi_add.html
│   │   │   │   ├── ooi_add_type_select.html
│   │   │   │   ├── ooi_delete.html
│   │   │   │   ├── ooi_detail.html
│   │   │   │   ├── ooi_detail_add_related_object.html
│   │   │   │   ├── ooi_detail_findings_list.html
│   │   │   │   ├── ooi_detail_findings_overview.html
│   │   │   │   ├── ooi_detail_object.html
│   │   │   │   ├── ooi_detail_origins_declarations.html
│   │   │   │   ├── ooi_detail_origins_inference.html
│   │   │   │   ├── ooi_detail_origins_observations.html
│   │   │   │   ├── ooi_edit.html
│   │   │   │   ├── ooi_findings.html
│   │   │   │   ├── ooi_list.html
│   │   │   │   ├── ooi_mute_finding.html
│   │   │   │   ├── ooi_page_tabs.html
│   │   │   │   ├── ooi_past_due_warning.html
│   │   │   │   ├── ooi_summary.html
│   │   │   │   └── ooi_tree.html
│   │   │   ├── organizations/
│   │   │   │   ├── organization_add.html
│   │   │   │   ├── organization_edit.html
│   │   │   │   ├── organization_list.html
│   │   │   │   ├── organization_member_add.html
│   │   │   │   ├── organization_member_add_account_type.html
│   │   │   │   ├── organization_member_add_header.html
│   │   │   │   ├── organization_member_edit.html
│   │   │   │   ├── organization_member_list.html
│   │   │   │   ├── organization_member_upload.html
│   │   │   │   ├── organization_settings.html
│   │   │   │   ├── organization_tags.html
│   │   │   │   └── select_clearance_level_radio_input.html
│   │   │   ├── partials/
│   │   │   │   ├── boefje_tile_option.html
│   │   │   │   ├── current_config.html
│   │   │   │   ├── delete_ooi_modal.html
│   │   │   │   ├── edit_ooi_clearance_level_modal.html
│   │   │   │   ├── elements/
│   │   │   │   │   ├── definition_list_item.html
│   │   │   │   │   ├── definition_list_items.html
│   │   │   │   │   ├── ooi_add_type_select_form.html
│   │   │   │   │   ├── ooi_detail_settings.html
│   │   │   │   │   ├── ooi_list_settings_form.html
│   │   │   │   │   ├── ooi_report_settings.html
│   │   │   │   │   ├── ooi_tree_condensed_table.html
│   │   │   │   │   ├── ooi_tree_condensed_table_row.html
│   │   │   │   │   ├── ooi_tree_table.html
│   │   │   │   │   └── ooi_tree_table_row.html
│   │   │   │   ├── explanations.html
│   │   │   │   ├── finding_occurrence_definition_list.html
│   │   │   │   ├── findings_list_toolbar.html
│   │   │   │   ├── form/
│   │   │   │   │   ├── boefje_tiles_form.html
│   │   │   │   │   ├── checkbox_group_table_form.html
│   │   │   │   │   ├── field_hidden_from_list.html
│   │   │   │   │   ├── field_input.html
│   │   │   │   │   ├── field_input_checkbox.html
│   │   │   │   │   ├── field_input_errors.html
│   │   │   │   │   ├── field_input_help_text.html
│   │   │   │   │   ├── field_input_hidden.html
│   │   │   │   │   ├── field_input_multiselect.html
│   │   │   │   │   ├── field_input_radio.html
│   │   │   │   │   ├── field_input_wrapper.html
│   │   │   │   │   ├── fieldset.html
│   │   │   │   │   ├── form_errors.html
│   │   │   │   │   └── indemnification_add_form.html
│   │   │   │   ├── hyperlink_ooi_id.html
│   │   │   │   ├── hyperlink_ooi_type.html
│   │   │   │   ├── language-switcher.html
│   │   │   │   ├── list_filters.html
│   │   │   │   ├── list_paginator.html
│   │   │   │   ├── mute_findings_modal.html
│   │   │   │   ├── notifications_block.html
│   │   │   │   ├── ooi_detail_related_object.html
│   │   │   │   ├── ooi_detail_toolbar.html
│   │   │   │   ├── ooi_head.html
│   │   │   │   ├── ooi_list_filters.html
│   │   │   │   ├── ooi_list_toolbar.html
│   │   │   │   ├── ooi_report_findings_block.html
│   │   │   │   ├── ooi_report_findings_block_table.html
│   │   │   │   ├── ooi_report_findings_block_table_expanded_row.html
│   │   │   │   ├── ooi_schedule.html
│   │   │   │   ├── ooi_summary_block.html
│   │   │   │   ├── ooi_summary_finding.html
│   │   │   │   ├── ooi_tree_toolbar_bottom.html
│   │   │   │   ├── organization_member_list_filters.html
│   │   │   │   ├── organization_properties_table.html
│   │   │   │   ├── organizations_menu_dropdown.html
│   │   │   │   ├── page-meta.html
│   │   │   │   ├── pagination.html
│   │   │   │   ├── scan_level_indicator.html
│   │   │   │   ├── secondary-menu.html
│   │   │   │   ├── skip-to-content.html
│   │   │   │   └── stepper.html
│   │   │   ├── rest_framework/
│   │   │   │   └── api.html
│   │   │   ├── robots.txt
│   │   │   ├── scan.html
│   │   │   ├── scan_profiles/
│   │   │   │   └── scan_profile_detail.html
│   │   │   ├── tasks/
│   │   │   │   ├── boefje_task_detail.html
│   │   │   │   ├── boefjes.html
│   │   │   │   ├── normalizers.html
│   │   │   │   ├── ooi_detail_task_list.html
│   │   │   │   ├── partials/
│   │   │   │   │   ├── stats.html
│   │   │   │   │   ├── tab_navigation.html
│   │   │   │   │   ├── task_actions.html
│   │   │   │   │   ├── task_filter.html
│   │   │   │   │   └── tasks_overview_header.html
│   │   │   │   ├── plugin_detail_task_list.html
│   │   │   │   └── reports.html
│   │   │   ├── two_factor/
│   │   │   │   ├── _base.html
│   │   │   │   ├── _base_focus.html
│   │   │   │   ├── _wizard_actions.html
│   │   │   │   ├── _wizard_forms.html
│   │   │   │   ├── core/
│   │   │   │   │   ├── backup_tokens.html
│   │   │   │   │   ├── login.html
│   │   │   │   │   ├── otp_required.html
│   │   │   │   │   ├── phone_register.html
│   │   │   │   │   ├── setup.html
│   │   │   │   │   └── setup_complete.html
│   │   │   │   ├── profile/
│   │   │   │   │   ├── disable.html
│   │   │   │   │   └── profile.html
│   │   │   │   └── twilio/
│   │   │   │       └── sms_message.html
│   │   │   ├── upload_csv.html
│   │   │   └── upload_raw.html
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   └── rocky.py
│   │   ├── urls.py
│   │   ├── version.py
│   │   ├── views/
│   │   │   ├── __init__.py
│   │   │   ├── bytes_raw.py
│   │   │   ├── clients.py
│   │   │   ├── finding_add.py
│   │   │   ├── finding_list.py
│   │   │   ├── finding_type_add.py
│   │   │   ├── handler403.py
│   │   │   ├── handler404.py
│   │   │   ├── health.py
│   │   │   ├── indemnification_add.py
│   │   │   ├── landing_page.py
│   │   │   ├── mixins.py
│   │   │   ├── ooi_add.py
│   │   │   ├── ooi_delete.py
│   │   │   ├── ooi_detail.py
│   │   │   ├── ooi_detail_related_object.py
│   │   │   ├── ooi_edit.py
│   │   │   ├── ooi_findings.py
│   │   │   ├── ooi_list.py
│   │   │   ├── ooi_mute.py
│   │   │   ├── ooi_tree.py
│   │   │   ├── ooi_view.py
│   │   │   ├── organization_add.py
│   │   │   ├── organization_edit.py
│   │   │   ├── organization_list.py
│   │   │   ├── organization_member_add.py
│   │   │   ├── organization_member_edit.py
│   │   │   ├── organization_member_list.py
│   │   │   ├── organization_settings.py
│   │   │   ├── page_actions.py
│   │   │   ├── privacy_statement.py
│   │   │   ├── rerun_bits.py
│   │   │   ├── scan_profile.py
│   │   │   ├── scans.py
│   │   │   ├── scheduler.py
│   │   │   ├── task_detail.py
│   │   │   ├── tasks.py
│   │   │   ├── upload_csv.py
│   │   │   └── upload_raw.py
│   │   └── wsgi.py
│   ├── setup.py
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── account/
│   │   │   ├── __init__.py
│   │   │   └── test_login.py
│   │   ├── conftest.py
│   │   ├── integration/
│   │   │   ├── __init__.py
│   │   │   ├── conftest.py
│   │   │   ├── test_bench.py
│   │   │   ├── test_report_runner.py
│   │   │   └── test_reports.py
│   │   ├── katalogus/
│   │   │   ├── __init__.py
│   │   │   ├── test_katalogus.py
│   │   │   ├── test_katalogus_boefje_setup.py
│   │   │   ├── test_katalogus_plugin_add.py
│   │   │   ├── test_katalogus_plugin_delete.py
│   │   │   └── test_katalogus_plugin_detail.py
│   │   ├── objects/
│   │   │   ├── __init__.py
│   │   │   ├── test_objects.py
│   │   │   ├── test_objects_add.py
│   │   │   ├── test_objects_delete.py
│   │   │   ├── test_objects_detail.py
│   │   │   ├── test_objects_edit.py
│   │   │   ├── test_objects_findings.py
│   │   │   ├── test_objects_graph.py
│   │   │   ├── test_objects_scan_profile.py
│   │   │   └── test_objects_tree.py
│   │   ├── onboarding/
│   │   │   ├── __init__.py
│   │   │   └── test_onboarding.py
│   │   ├── reports/
│   │   │   ├── __init__.py
│   │   │   ├── test_aggregate_report_flow.py
│   │   │   ├── test_base_report.py
│   │   │   ├── test_dns_report.py
│   │   │   ├── test_findings_report.py
│   │   │   ├── test_generate_report_flow.py
│   │   │   ├── test_history_report.py
│   │   │   ├── test_ipv6_report.py
│   │   │   ├── test_mail_report.py
│   │   │   ├── test_multi_report_flow.py
│   │   │   ├── test_name_server_report.py
│   │   │   ├── test_open_ports_report.py
│   │   │   ├── test_plugins.py
│   │   │   ├── test_report_overview.py
│   │   │   ├── test_report_schedules.py
│   │   │   ├── test_report_tasks.py
│   │   │   ├── test_rpki_report.py
│   │   │   ├── test_safe_connections_report.py
│   │   │   ├── test_systems_report.py
│   │   │   ├── test_tls_report.py
│   │   │   ├── test_vulnerability_report.py
│   │   │   └── test_web_systems_report.py
│   │   ├── robot/
│   │   │   ├── ci/
│   │   │   │   └── 01_rocky_loads.robot
│   │   │   ├── complete_onboarding/
│   │   │   │   ├── 01_onboard_with_all_users.robot
│   │   │   │   ├── 02_login_as_redteamer_and_do_stuff.robot
│   │   │   │   └── 03_functional_tests.robot
│   │   │   ├── skip_onboarding_no_report/
│   │   │   │   └── 01_skip_onboarding_no_report.robot
│   │   │   ├── skip_onboarding_with_functional_tests/
│   │   │   │   ├── 01_skip_onboarding_with_functional_tests.robot
│   │   │   │   └── 02_functional_tests.robot
│   │   │   ├── skip_onboarding_with_report/
│   │   │   │   └── 01_skip_onboarding_with_report.robot
│   │   │   └── xxx.resource
│   │   ├── scheduler/
│   │   │   ├── __init__.py
│   │   │   └── test_scheduler_errors.py
│   │   ├── stubs/
│   │   │   ├── aggregate_report_data.json
│   │   │   ├── iana_service.html
│   │   │   ├── katalogus_boefjes.json
│   │   │   ├── katalogus_normalizers.json
│   │   │   ├── mock.csv
│   │   │   ├── multi_report_data_minvws.json
│   │   │   ├── multi_report_data_mispoes.json
│   │   │   ├── multi_report_post_processed_data.json
│   │   │   ├── question_schema.json
│   │   │   ├── snyk_response.html
│   │   │   └── wiki.html
│   │   ├── test_admin.py
│   │   ├── test_api.py
│   │   ├── test_api_organization.py
│   │   ├── test_boefjes_tasks.py
│   │   ├── test_core.py
│   │   ├── test_dashboard.py
│   │   ├── test_findings_add.py
│   │   ├── test_groups_and_permissions.py
│   │   ├── test_health.py
│   │   ├── test_indemnification.py
│   │   ├── test_landing_page.py
│   │   ├── test_members.py
│   │   ├── test_migrations.py
│   │   ├── test_models.py
│   │   ├── test_observed_at.py
│   │   ├── test_ooi_information.py
│   │   ├── test_organization.py
│   │   ├── test_privacy_statement.py
│   │   ├── test_scans.py
│   │   ├── test_upload_csv.py
│   │   ├── test_upload_members.py
│   │   ├── test_upload_raw.py
│   │   ├── test_validators.py
│   │   └── test_zip.py
│   ├── tools/
│   │   ├── __init__.py
│   │   ├── add_ooi_information.py
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── context_processors.py
│   │   ├── enums.py
│   │   ├── fields.py
│   │   ├── forms/
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── boefje.py
│   │   │   ├── finding_type.py
│   │   │   ├── findings.py
│   │   │   ├── ooi.py
│   │   │   ├── ooi_form.py
│   │   │   ├── scheduler.py
│   │   │   ├── settings.py
│   │   │   ├── upload_csv.py
│   │   │   ├── upload_oois.py
│   │   │   └── upload_raw.py
│   │   ├── management/
│   │   │   ├── __init__.py
│   │   │   └── commands/
│   │   │       ├── __init__.py
│   │   │       ├── almost_flush.py
│   │   │       ├── export_migrations.py
│   │   │       ├── makemessages.py
│   │   │       ├── setup_dev_account.py
│   │   │       └── setup_test_org.py
│   │   ├── migrations/
│   │   │   ├── 0001_initial.py
│   │   │   ├── 0001_squashed_0041_merge_20230731_1131.py
│   │   │   ├── 0002_alter_organization_octopoes_host.py
│   │   │   ├── 0003_change_orgazation_host_to_code.py
│   │   │   ├── 0004_ooiinformation.py
│   │   │   ├── 0005_scanprofile.py
│   │   │   ├── 0006_alter_organization_name.py
│   │   │   ├── 0007_update_job.py
│   │   │   ├── 0008_organizationmember_verified.py
│   │   │   ├── 0009_scanprofile_is_source_ooi.py
│   │   │   ├── 0010_alter_scanprofile_reference.py
│   │   │   ├── 0011_job_input_ooi.py
│   │   │   ├── 0012_rename_module_job_boefje_name.py
│   │   │   ├── 0013_boefjeconfig.py
│   │   │   ├── 0014_drop_dispatches_field.py
│   │   │   ├── 0015_alter_job_input_ooi.py
│   │   │   ├── 0016_organization_signal_fields.py
│   │   │   ├── 0017_alter_organizationmember_foreignkey.py
│   │   │   ├── 0018_alter_boefjeconfig_options.py
│   │   │   ├── 0019_alter_scanprofile_remove_level_and_user.py
│   │   │   ├── 0020_auto_20220524_1324.py
│   │   │   ├── 0021_delete_boefjeconfig.py
│   │   │   ├── 0022_alter_organization_options.py
│   │   │   ├── 0023_delete_scanprofile.py
│   │   │   ├── 0024_auto_20221005_1251.py
│   │   │   ├── 0025_auto_20221027_1233.py
│   │   │   ├── 0026_auto_20221031_1344.py
│   │   │   ├── 0027_auto_20230103_1721.py
│   │   │   ├── 0028_auto_20230117_1242.py
│   │   │   ├── 0029_alter_organizationtag_color.py
│   │   │   ├── 0029_set_user_full_name.py
│   │   │   ├── 0030_auto_20230227_1458.py
│   │   │   ├── 0031_merge_20230301_2012.py
│   │   │   ├── 0032_alter_organizationmember_user.py
│   │   │   ├── 0033_alter_organization_options.py
│   │   │   ├── 0033_auto_20230407_1113.py
│   │   │   ├── 0034_alter_organization_options.py
│   │   │   ├── 0034_organizationmember_groups.py
│   │   │   ├── 0035_update_ooi_delete_perm.py
│   │   │   ├── 0035_update_perms_move_and_clear_groups.py
│   │   │   ├── 0036_merge_20230504_1629.py
│   │   │   ├── 0037_alter_organization_options.py
│   │   │   ├── 0038_alter_organization_options.py
│   │   │   ├── 0038_delete_job.py
│   │   │   ├── 0039_merge_0038_alter_organization_options_0038_delete_job.py
│   │   │   ├── 0039_update_permissions.py
│   │   │   ├── 0040_admin_inherits_red_teamer_permissions.py
│   │   │   ├── 0040_update_admin_permission.py
│   │   │   ├── 0041_merge_20230731_1131.py
│   │   │   ├── 0042_alter_organization_options.py
│   │   │   ├── 0043_alter_organization_options.py
│   │   │   ├── 0044_alter_organization_options.py
│   │   │   ├── 0045_alter_organization_options.py
│   │   │   ├── 0045_update_permissions.py
│   │   │   ├── 0046_alter_organization_options.py
│   │   │   ├── 0047_alter_organization_code_alter_organization_name.py
│   │   │   └── __init__.py
│   │   ├── models.py
│   │   ├── ooi_helpers.py
│   │   ├── permissions.py
│   │   ├── serializers.py
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   ├── form_extra.py
│   │   │   ├── media_with_nonce.py
│   │   │   └── ooi_extra.py
│   │   ├── validators.py
│   │   ├── view_helpers.py
│   │   └── viewsets.py
│   └── whitelist.py
├── scripts/
│   ├── backup/
│   │   ├── backup-volumes.sh
│   │   └── restore-volumes.sh
│   ├── development/
│   │   └── backport.py
│   ├── installation/
│   │   ├── openkat-empty-job-queue.sh
│   │   ├── openkat-install.sh
│   │   ├── openkat-reset.sh
│   │   ├── openkat-restart.sh
│   │   ├── openkat-show-journal.sh
│   │   ├── openkat-start.sh
│   │   ├── openkat-status.sh
│   │   ├── openkat-stop.sh
│   │   └── openkat-update.sh
│   ├── migrate-openkat.sh
│   └── migrate.md
└── sonar-project.properties
Download .txt
Showing preview only (505K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (5266 symbols across 846 files)

FILE: .github/scripts/coverage_file_fixer.py
  function path_prefixer (line 13) | def path_prefixer(file: Path, prefix: Path) -> None:
  function main (line 25) | def main():

FILE: boefjes/boefjes/__main__.py
  function get_runtime_manager (line 24) | def get_runtime_manager(
  function cli (line 58) | def cli(queue: str, worker: bool, images: tuple[str] | None, plugins: tu...

FILE: boefjes/boefjes/api.py
  class UvicornServer (line 26) | class UvicornServer(ForkProcess):
    method __init__ (line 27) | def __init__(self, config: Config):
    method stop (line 32) | def stop(self) -> None:
    method run (line 35) | def run(self, *args, **kwargs):
  function run (line 39) | def run():
  class TaskIn (line 47) | class TaskIn(BaseModel):
  function get_scheduler_client (line 51) | def get_scheduler_client(plugin_service=Depends(get_plugin_service)):
  function get_bytes_client (line 55) | def get_bytes_client():
  function root (line 60) | async def root():
  function boefje_input (line 65) | def boefje_input(task_id: UUID, scheduler_client: SchedulerAPIClient = D...
  function boefje_output (line 76) | def boefje_output(
  function get_task_from_scheduler (line 116) | def get_task_from_scheduler(task_id: UUID, scheduler_client: SchedulerAP...
  function pop_tasks (line 132) | def pop_tasks(
  function push_item (line 142) | def push_item(
  function patch_task (line 149) | def patch_task(
  function get_task (line 156) | def get_task(task_id: uuid.UUID, scheduler_client: SchedulerAPIClient = ...

FILE: boefjes/boefjes/clients/bytes_client.py
  function retry_with_login (line 21) | def retry_with_login(function: ClientSessionMethod) -> ClientSessionMethod:
  class BytesAPIClient (line 36) | class BytesAPIClient(BoefjeStorageInterface):
    method __init__ (line 37) | def __init__(self, base_url: str, username: str, password: str):
    method login (line 48) | def login(self) -> None:
    method _verify_response (line 52) | def _verify_response(response: Response) -> None:
    method _get_authentication_headers (line 62) | def _get_authentication_headers(self) -> dict[str, str]:
    method _get_token (line 65) | def _get_token(self) -> str:
    method save_boefje_meta (line 73) | def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:
    method get_boefje_meta (line 81) | def get_boefje_meta(self, boefje_meta_id: str) -> BoefjeMeta:
    method save_normalizer_meta (line 88) | def save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:
    method get_normalizer_meta (line 96) | def get_normalizer_meta(self, normalizer_meta_id: uuid.UUID) -> Normal...
    method save_output (line 103) | def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOu...
    method save_raws (line 112) | def save_raws(self, boefje_meta_id: uuid.UUID, boefje_output: BoefjeOu...
    method save_raw (line 124) | def save_raw(self, boefje_meta_id: str, raw: str | bytes, mime_types: ...
    method get_raw (line 146) | def get_raw(self, raw_data_id: str) -> bytes:
    method get_raws (line 153) | def get_raws(self, boefje_meta_id: str) -> BoefjeOutput:
    method get_raw_meta (line 160) | def get_raw_meta(self, raw_data_id: str) -> RawDataMeta:

FILE: boefjes/boefjes/clients/scheduler_client.py
  class SchedulerAPIClient (line 25) | class SchedulerAPIClient(SchedulerClientInterface):
    method __init__ (line 26) | def __init__(
    method _verify_response (line 41) | def _verify_response(response: Response) -> None:
    method pop_items (line 44) | def pop_items(
    method push_item (line 89) | def push_item(self, p_item: Task) -> None:
    method patch_task (line 96) | def patch_task(self, task_id: uuid.UUID, status: TaskStatus) -> None:
    method get_task (line 100) | def get_task(self, task_id: uuid.UUID, hydrate: bool = True) -> Task:
    method _hydrate_boefje_meta (line 111) | def _hydrate_boefje_meta(self, boefje_meta: BoefjeMeta) -> BoefjeMeta:
  function boefje_env_variables (line 145) | def boefje_env_variables() -> dict:
  function get_system_env_settings_for_boefje (line 159) | def get_system_env_settings_for_boefje(allowed_keys: list[str]) -> dict:
  function get_environment_settings (line 163) | def get_environment_settings(boefje_meta: BoefjeMeta, schema: dict | Non...
  function get_octopoes_api_connector (line 192) | def get_octopoes_api_connector(org_code: str) -> OctopoesAPIConnector:

FILE: boefjes/boefjes/config.py
  class EncryptionMiddleware (line 18) | class EncryptionMiddleware(Enum):
  class BackwardsCompatibleEnvSettings (line 23) | class BackwardsCompatibleEnvSettings(EnvSettingsSource):
    method __call__ (line 35) | def __call__(self) -> dict[str, Any]:
  class Settings (line 52) | class Settings(BaseSettings):
    method settings_customise_sources (line 145) | def settings_customise_sources(

FILE: boefjes/boefjes/dependencies/encryption.py
  class EncryptMiddleware (line 7) | class EncryptMiddleware(abc.ABC):
    method encode (line 9) | def encode(self, contents: str) -> str:
    method decode (line 13) | def decode(self, contents: str) -> str:
  class IdentityMiddleware (line 17) | class IdentityMiddleware(EncryptMiddleware):
    method encode (line 18) | def encode(self, contents: str) -> str:
    method decode (line 21) | def decode(self, contents: str) -> str:
  class NaclBoxMiddleware (line 25) | class NaclBoxMiddleware(EncryptMiddleware):
    method __init__ (line 30) | def __init__(self, private_key: str, public_key: str):
    method encode (line 35) | def encode(self, contents: str) -> str:
    method decode (line 40) | def decode(self, contents: str) -> str:

FILE: boefjes/boefjes/dependencies/plugins.py
  class PluginService (line 27) | class PluginService:
    method __init__ (line 28) | def __init__(self, plugin_storage: PluginStorage, config_storage: Conf...
    method __enter__ (line 33) | def __enter__(self):
    method __exit__ (line 39) | def __exit__(self, exc_type, exc_val, exc_tb):
    method get_all (line 43) | def get_all(self, organisation_id: str) -> list[PluginType]:
    method _get_all_without_enabled (line 55) | def _get_all_without_enabled(self) -> dict[str, PluginType]:
    method by_plugin_id (line 63) | def by_plugin_id(self, plugin_id: str, organisation_id: str) -> Plugin...
    method by_plugin_ids (line 72) | def by_plugin_ids(self, plugin_ids: list[str], organisation_id: str) -...
    method get_all_settings (line 85) | def get_all_settings(self, organisation_id: str, plugin_id: str):
    method clone_settings_to_organisation (line 88) | def clone_settings_to_organisation(self, from_organisation: str, to_or...
    method upsert_settings (line 112) | def upsert_settings(self, settings: dict, organisation_id: str, plugin...
    method create_boefje (line 118) | def create_boefje(self, boefje: Boefje) -> None:
    method create_normalizer (line 133) | def create_normalizer(self, normalizer: Normalizer) -> None:
    method _put_boefje (line 148) | def _put_boefje(self, boefje_id: str) -> None:
    method _put_normalizer (line 163) | def _put_normalizer(self, normalizer_id: str) -> None:
    method delete_settings (line 178) | def delete_settings(self, organisation_id: str, plugin_id: str):
    method schema (line 183) | def schema(self, plugin_id: str) -> dict | None:
    method cover (line 191) | def cover(self, plugin_id: str) -> Path:
    method description (line 197) | def description(self, plugin_id: str, organisation_id: str) -> str:
    method set_enabled_by_id (line 209) | def set_enabled_by_id(self, plugin_id: str, organisation_id: str, enab...
    method _assert_settings_match_schema (line 219) | def _assert_settings_match_schema(self, all_settings: dict, plugin_id:...
  function get_plugin_service (line 229) | def get_plugin_service() -> Iterator[PluginService]:
  function get_pagination_parameters (line 236) | def get_pagination_parameters(offset: int = 0, limit: int | None = None)...
  function get_plugins_filter_parameters (line 240) | def get_plugins_filter_parameters(

FILE: boefjes/boefjes/job_handler.py
  class DockerBoefjeHandler (line 40) | class DockerBoefjeHandler(BoefjeHandler):
    method __init__ (line 44) | def __init__(self, scheduler_client: SchedulerAPIClient, bytes_api_cli...
    method handle (line 49) | def handle(self, task: Task) -> tuple[BoefjeMeta, BoefjeOutput] | None...
    method copy_raw_files (line 119) | def copy_raw_files(
  class LocalNormalizerHandler (line 131) | class LocalNormalizerHandler(NormalizerHandler):
    method __init__ (line 132) | def __init__(
    method handle (line 144) | def handle(self, task: Task):

FILE: boefjes/boefjes/katalogus/configs.py
  function list_configs (line 16) | def list_configs(

FILE: boefjes/boefjes/katalogus/organisations.py
  function list_organisations (line 12) | def list_organisations(storage: OrganisationStorage = Depends(get_organi...
  function get_organisation (line 18) | def get_organisation(organisation_id: str, storage: OrganisationStorage ...
  function add_organisation (line 27) | def add_organisation(organisation: Organisation, storage: OrganisationSt...
  function upsert_organisation (line 33) | def upsert_organisation(organisation: Organisation, storage: Organisatio...
  function remove_organisation (line 39) | def remove_organisation(organisation_id: str, storage: OrganisationStora...

FILE: boefjes/boefjes/katalogus/plugins.py
  function _plugin_matches_query (line 28) | def _plugin_matches_query(plugin: PluginType, query: str) -> bool:
  function list_plugins (line 38) | def list_plugins(
  function get_plugin (line 89) | def get_plugin(
  function add_plugin (line 100) | def add_plugin(plugin: PluginType, plugin_service: PluginService = Depen...
  function update_plugin_state (line 121) | def update_plugin_state(
  class BoefjeIn (line 131) | class BoefjeIn(BaseModel):
    method json_schema_valid (line 153) | def json_schema_valid(cls, schema: dict | None) -> dict | None:
    method cron_valid (line 164) | def cron_valid(cls, cron: str | None) -> str | None:
  function update_boefje (line 172) | def update_boefje(boefje_id: str, boefje: BoefjeIn, storage: PluginStora...
  function delete_boefje (line 185) | def delete_boefje(boefje_id: str, plugin_storage: PluginStorage = Depend...
  class NormalizerIn (line 190) | class NormalizerIn(BaseModel):
  function update_normalizer (line 205) | def update_normalizer(
  function delete_normalizer (line 213) | def delete_normalizer(normalizer_id: str, plugin_storage: PluginStorage ...
  function get_plugin_schema (line 219) | def get_plugin_schema(plugin_id: str, plugin_service: PluginService = De...
  function get_plugin_cover (line 225) | def get_plugin_cover(plugin_id: str, plugin_service: PluginService = Dep...
  function get_plugin_description (line 230) | def get_plugin_description(
  function clone_settings (line 237) | def clone_settings(organisation_id: str, to_org: str, storage: PluginSer...

FILE: boefjes/boefjes/katalogus/root.py
  function entity_not_found_handler (line 54) | def entity_not_found_handler(request: Request, exc: NotFound) -> JSONRes...
  function not_allowed_handler (line 59) | def not_allowed_handler(request: Request, exc: NotAllowed) -> JSONResponse:
  function integrity_error_handler (line 64) | def integrity_error_handler(request: Request, exc: IntegrityError) -> JS...
  function storage_error_handler (line 69) | def storage_error_handler(request: Request, exc: StorageError) -> JSONRe...
  class ServiceHealth (line 73) | class ServiceHealth(BaseModel):
  function root (line 85) | def root() -> RedirectResponse:
  function health (line 90) | def health() -> ServiceHealth:
  function v1_root (line 95) | def v1_root() -> RedirectResponse:

FILE: boefjes/boefjes/katalogus/settings.py
  function list_settings (line 9) | def list_settings(
  function upsert_settings (line 17) | def upsert_settings(
  function remove_settings (line 25) | def remove_settings(

FILE: boefjes/boefjes/local/runner.py
  class LocalNormalizerJobRunner (line 21) | class LocalNormalizerJobRunner(NormalizerJobRunner):
    method __init__ (line 22) | def __init__(self, local_repository: LocalPluginRepository):
    method run (line 25) | def run(self, normalizer_meta: NormalizerMeta, raw: bytes) -> Normaliz...
    method _parse_results (line 46) | def _parse_results(self, normalizer_meta: NormalizerMeta, results: Ite...

FILE: boefjes/boefjes/logging.py
  function configure_logging (line 12) | def configure_logging():

FILE: boefjes/boefjes/migrations/env.py
  function run_migrations_offline (line 28) | def run_migrations_offline() -> None:
  function run_migrations_online (line 53) | def run_migrations_online() -> None:

FILE: boefjes/boefjes/migrations/versions/0001_add_katalogus_models.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 67) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/0002_change_lengths_of_several_char_fields.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 42) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/0003_longer_plugin_ids.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 34) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/197672984df0_make_organisation_code_field_larger.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 27) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/5be152459a7b_introduce_schema_field_to_boefje_model.py
  function upgrade (line 26) | def upgrade() -> None:
  function downgrade (line 48) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/6f99834a4a5a_introduce_boefje_and_normalizer_models.py
  function upgrade (line 22) | def upgrade() -> None:
  function downgrade (line 59) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/7c88b9cd96aa_remove_the_repository_model.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 39) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/870fc302b852_remove_environment_keys_field.py
  function upgrade (line 20) | def upgrade() -> None:
  function downgrade (line 27) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/9f48560b0000_add_schedule_interval_fields.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 26) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/a2c8d54b0124_unique_plugin_names.py
  function upgrade (line 18) | def upgrade() -> None:
  function downgrade (line 25) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/cd34fdfafdaf_json_settings_for_settings_table.py
  function upgrade (line 26) | def upgrade() -> None:
  function upgrade_encrypted_settings (line 45) | def upgrade_encrypted_settings(conn: Connection) -> None:
  function downgrade (line 75) | def downgrade() -> None:
  function downgrade_encrypted_settings (line 97) | def downgrade_encrypted_settings(conn: Connection) -> None:

FILE: boefjes/boefjes/migrations/versions/f9de6eb7824b_introduce_boefjeconfig_model.py
  function upgrade (line 30) | def upgrade() -> None:
  function downgrade (line 225) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/fc0295b38184_add_run_on_field_to_boefje.py
  function upgrade (line 22) | def upgrade() -> None:
  function downgrade (line 29) | def downgrade() -> None:

FILE: boefjes/boefjes/migrations/versions/fdeaea4481b8_add_deduplication_flag.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 26) | def downgrade() -> None:

FILE: boefjes/boefjes/normalizer_interfaces.py
  class NormalizerJobRunner (line 5) | class NormalizerJobRunner:
    method run (line 6) | def run(self, normalizer_meta: NormalizerMeta, raw: bytes) -> Normaliz...

FILE: boefjes/boefjes/normalizer_models.py
  class NormalizerObservation (line 10) | class NormalizerObservation(BaseModel):
  class NormalizerDeclaration (line 16) | class NormalizerDeclaration(BaseModel):
  class NormalizerAffirmation (line 22) | class NormalizerAffirmation(BaseModel):
  class NormalizerResults (line 27) | class NormalizerResults(BaseModel):

FILE: boefjes/boefjes/plugins/helpers.py
  function cpe_to_name_version (line 1) | def cpe_to_name_version(cpe: str) -> tuple[str | None, str | None]:

FILE: boefjes/boefjes/plugins/kat_adr_finding_types/main.py
  function run (line 6) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_adr_finding_types/normalize.py
  function run (line 20) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_adr_validator/main.py
  function run (line 4) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_adr_validator/normalize.py
  function run (line 10) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_answer_parser/normalize.py
  function run (line 8) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_binaryedge/containers/normalize.py
  function run (line 12) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_binaryedge/databases/normalize.py
  function run (line 12) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_binaryedge/http_web/normalize.py
  function run (line 12) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_binaryedge/main.py
  function run (line 8) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_binaryedge/message_queues/normalize.py
  function run (line 12) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_binaryedge/protocols/normalize.py
  function run (line 11) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_binaryedge/remote_desktop/normalize.py
  function run (line 12) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_binaryedge/service_identification/normalize.py
  function run (line 14) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_binaryedge/services/normalize.py
  function run (line 12) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_burpsuite/normalize.py
  function find_network (line 17) | def find_network(data: dict) -> dict:
  function run (line 29) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_calvin/normalize.py
  function run (line 8) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:
  function parse_log (line 15) | def parse_log(log: dict) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_censys/main.py
  function run (line 6) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_censys/normalize.py
  function run (line 15) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_crt_sh/main.py
  function request_certs (line 32) | def request_certs(
  function run (line 62) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_crt_sh/normalize.py
  function run (line 13) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_cve_2023_34039/main.py
  function run (line 21) | def run(boefje_meta: dict) -> list[tuple[set, str | bytes]]:

FILE: boefjes/boefjes/plugins/kat_cve_2023_35078/main.py
  function run (line 8) | def run(boefje_meta: dict) -> list[tuple[set, str | bytes]]:

FILE: boefjes/boefjes/plugins/kat_cve_2023_35078/normalize.py
  function extract_js_version (line 12) | def extract_js_version(html_content: str) -> Version | bool:
  function extract_css_version (line 26) | def extract_css_version(html_content: str) -> Version | bool:
  function strip_vsp_and_build (line 40) | def strip_vsp_and_build(url: str) -> Iterable[str]:
  function is_vulnerable_version (line 50) | def is_vulnerable_version(vulnerable_ranges: list[tuple[Version, Version...
  function run (line 54) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_cve_2024_6387/normalize.py
  function is_vulnerable (line 31) | def is_vulnerable(banner: str) -> bool:
  function run (line 55) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_cve_finding_types/main.py
  function run (line 6) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_cve_finding_types/normalize.py
  function get_risk_level (line 20) | def get_risk_level(severity_score):
  function run (line 27) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_cwe_finding_types/main.py
  function run (line 8) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_cwe_finding_types/normalize.py
  function run (line 11) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_dicom/main.py
  function run (line 7) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_dicom/normalize.py
  function run (line 11) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_dns/main.py
  class TimeoutException (line 17) | class TimeoutException(Exception):
  class ZoneNotFoundException (line 21) | class ZoneNotFoundException(Exception):
  function get_record_types (line 25) | def get_record_types() -> set[str]:
  function run (line 35) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:
  function get_parent_zone_soa (line 70) | def get_parent_zone_soa(resolver: dns.resolver.Resolver, name: Name) -> ...
  function get_email_security_records (line 83) | def get_email_security_records(resolver: dns.resolver.Resolver, hostname...

FILE: boefjes/boefjes/plugins/kat_dns/normalize.py
  function run (line 35) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_dns_version/main.py
  function run (line 11) | def run(boefje_meta: dict) -> list[tuple[set, str | bytes]]:

FILE: boefjes/boefjes/plugins/kat_dns_version/normalize.py
  function run (line 9) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_dns_zone/main.py
  class ZoneNotFoundException (line 12) | class ZoneNotFoundException(Exception):
  function run (line 16) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:
  function get_parent_zone_soa (line 31) | def get_parent_zone_soa(name: Name) -> Answer:

FILE: boefjes/boefjes/plugins/kat_dns_zone/normalize.py
  function run (line 12) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_dnssec/main.py
  function run_drill (line 5) | def run_drill(domain: str, record_type: str) -> bytes:
  function run (line 14) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_dnssec/normalize.py
  function run (line 8) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_export_http/main.py
  function run (line 10) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_external_db/main.py
  function run (line 8) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_external_db/normalize.py
  function follow_path_in_dict (line 24) | def follow_path_in_dict(path, path_dict):
  function get_indemnification_level (line 34) | def get_indemnification_level(path_dict):
  function run (line 46) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_fierce/fierce.py
  function fatal (line 31) | def fatal(msg, _return_code=-1):
  function print_subdomain_result (line 35) | def print_subdomain_result(url, ip, nearby=None):
  function unvisited_closure (line 44) | def unvisited_closure():
  function find_subdomain_list_file (line 56) | def find_subdomain_list_file(filename):
  function head_request (line 66) | def head_request(url, timeout=2):
  function concatenate_subdomains (line 79) | def concatenate_subdomains(domain, subdomains):
  function query (line 90) | def query(resolver, domain, record_type="A", tcp=False):
  function reverse_query (line 109) | def reverse_query(resolver, ip, tcp=False):
  function recursive_query (line 113) | def recursive_query(resolver, domain, record_type="NS", tcp=False):
  function zone_transfer (line 126) | def zone_transfer(address, domain):
  function get_class_c_network (line 133) | def get_class_c_network(ip):
  function default_expander (line 141) | def default_expander(ip):
  function traverse_expander (line 145) | def traverse_expander(ip, n=5):
  function wide_expander (line 155) | def wide_expander(ip):
  function range_expander (line 163) | def range_expander(ip):
  function default_filter (line 174) | def default_filter(address):
  function search_filter (line 178) | def search_filter(domains, address):
  function find_nearby (line 182) | def find_nearby(resolver, ips, filter_func=None):
  function get_stripped_file_lines (line 204) | def get_stripped_file_lines(filename):
  function get_subdomains (line 217) | def get_subdomains(subdomains, subdomain_filename):
  function update_resolver_nameservers (line 231) | def update_resolver_nameservers(resolver, nameservers, nameserver_filena...
  function fierce (line 250) | def fierce(**kwargs):
  function parse_args (line 337) | def parse_args(args):
  function main (line 385) | def main():

FILE: boefjes/boefjes/plugins/kat_fierce/main.py
  function run (line 6) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_fierce/normalize.py
  function run (line 12) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_finding_normalizer/normalize.py
  function run (line 11) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_green_hosting/main.py
  function run (line 6) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_green_hosting/normalize.py
  function run (line 9) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_kat_finding_types/main.py
  function run (line 6) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_kat_finding_types/normalize.py
  function run (line 20) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_leakix/main.py
  function get_api_headers (line 10) | def get_api_headers() -> dict:
  function get_host_results (line 15) | def get_host_results(ip: str) -> list[dict]:
  function get_search_results (line 40) | def get_search_results(query: str) -> list[dict]:
  function run (line 70) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_leakix/normalize.py
  function run (line 38) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:
  function handle_autonomous_system (line 111) | def handle_autonomous_system(event):
  function handle_ip (line 117) | def handle_ip(event, network_reference, as_ooi_reference):
  function handle_hostname (line 146) | def handle_hostname(event, network_reference):
  function handle_software (line 157) | def handle_software(event, event_ooi_reference):
  function handle_leak (line 177) | def handle_leak(event, event_ooi_reference, software_ooi):
  function handle_tag (line 217) | def handle_tag(event, software_ooi_reference=None, ip_port_ooi_reference...

FILE: boefjes/boefjes/plugins/kat_manual/csv/normalize.py
  function run (line 27) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:
  function process_csv (line 33) | def process_csv(csv_raw_data: bytes, reference_cache: dict) -> Iterable[...
  function get_object_type (line 52) | def get_object_type(csv_data: io.StringIO) -> str:
  function get_ooi_from_csv (line 76) | def get_ooi_from_csv(
  function get_or_create_reference (line 112) | def get_or_create_reference(ooi_type_name: str, value: str | None, refer...

FILE: boefjes/boefjes/plugins/kat_manual/single_ooi/normalize.py
  function run (line 7) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_masscan/main.py
  function run_masscan (line 7) | def run_masscan(target_ip: str):
  function run (line 26) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_masscan/normalize.py
  function get_ip_ports_and_service (line 11) | def get_ip_ports_and_service(ip_with_ports: dict, network: Network, netb...
  function run (line 36) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_maxmind_geoip/main.py
  function run (line 30) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:
  function create_hash (line 53) | def create_hash(data: bytes, algo: str) -> str:
  function cache_out_of_date (line 58) | def cache_out_of_date() -> bool:
  function refresh_geoip (line 68) | def refresh_geoip(algo: str) -> dict:
  function find_geoip_path (line 104) | def find_geoip_path() -> str:
  function geoip_file_exists (line 111) | def geoip_file_exists() -> bool:
  function remove_old_geolite_data (line 120) | def remove_old_geolite_data():

FILE: boefjes/boefjes/plugins/kat_maxmind_geoip/normalize.py
  function run (line 9) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_nikto/main.js
  function get_config_content (line 8) | function get_config_content(scheme) {

FILE: boefjes/boefjes/plugins/kat_nikto/normalize.py
  function _extract_missing_header (line 54) | def _extract_missing_header(msg: str) -> str | None:
  function _outdated_software (line 59) | def _outdated_software(msg: str, ooi_ref: Reference) -> Iterable[Normali...
  function _missing_header (line 74) | def _missing_header(msg: str, ooi_ref: Reference) -> Iterable[Normalizer...
  function _banner_disclosed (line 80) | def _banner_disclosed(msg: str, ooi_ref: Reference) -> Iterable[Normaliz...
  function _simple_finding (line 90) | def _simple_finding(finding_type_id: str, msg: str, ooi_ref: Reference) ...
  function _generic_finding (line 96) | def _generic_finding(msg: str, ooi_ref: Reference) -> Iterable[Normalize...
  function scan_nikto_output (line 100) | def scan_nikto_output(data: list[dict[str, Any]], ooi_ref: Reference) ->...
  function run (line 130) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_nikto/oci_adapter.js
  function b64encode (line 7) | function b64encode(inp) {
  function main (line 11) | async function main() {

FILE: boefjes/boefjes/plugins/kat_nmap_tcp/main.py
  class UnacceptedVSLM (line 17) | class UnacceptedVSLM(Exception):
  function validate_ports (line 21) | def validate_ports(ports: str | None) -> str:
  function parse_input_ooi (line 49) | def parse_input_ooi(input_ooi: dict) -> IPv4Address | IPv6Address | IPv4...
  function run (line 69) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_nmap_tcp/normalize.py
  function get_ip_ports_and_service (line 13) | def get_ip_ports_and_service(host: NmapHost, network: Network, netblock:...
  function run (line 55) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_nuclei_cve/main.py
  function get_target_url (line 4) | def get_target_url(input_ooi: dict) -> str:
  function run (line 9) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_nuclei_cve/normalize.py
  function run (line 9) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_nuclei_exposed_panels/normalize.py
  function run (line 9) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_nuclei_take_over/normalize.py
  function run (line 9) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_pdio_subfinder/main.py
  function run (line 6) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_pdio_subfinder/normalize.py
  function run (line 8) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_rdns/main.py
  function run (line 8) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_rdns/normalize.py
  function run (line 14) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_report_data/normalize.py
  function run (line 7) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_retirejs_finding_types/main.py
  function run (line 4) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_retirejs_finding_types/normalize.py
  function _hash_identifiers (line 21) | def _hash_identifiers(identifiers: dict[str, str | list[str]]) -> str:
  function _create_description (line 28) | def _create_description(finding: dict) -> str:
  function run (line 44) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_rpki/main.py
  function run (line 35) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:
  function cache_out_of_date (line 106) | def cache_out_of_date(meta_path: Path) -> bool:
  function refresh_cache (line 119) | def refresh_cache(
  function create_hash (line 154) | def create_hash(data: bytes, algo: str) -> str:
  function load_json (line 159) | def load_json(path: Path) -> dict:
  function load_jsonl (line 165) | def load_jsonl(path: Path) -> list:

FILE: boefjes/boefjes/plugins/kat_rpki/normalize.py
  function run (line 10) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_security_txt_downloader/main.py
  function run (line 12) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:
  function do_request (line 64) | def do_request(hostname: str, session: Session, uri: str, useragent: str...

FILE: boefjes/boefjes/plugins/kat_security_txt_downloader/normalize.py
  function run (line 14) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_service_banner/main.py
  function get_sock (line 8) | def get_sock(ip, port, timeout):
  function get_banner (line 19) | def get_banner(sock, bytescount):
  function run (line 34) | def run(boefje_meta: dict) -> list[tuple[set, str | bytes]]:

FILE: boefjes/boefjes/plugins/kat_shodan/main.py
  function run (line 9) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_shodan/normalize.py
  function run (line 11) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_shodan_internetdb/main.py
  function run (line 9) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_shodan_internetdb/normalize.py
  function run (line 16) | def run(input_ooi: dict, raw: bytes) -> Iterable[OOI]:

FILE: boefjes/boefjes/plugins/kat_snyk/check_version.py
  class VersionCheck (line 8) | class VersionCheck(Enum):
  function check_version (line 18) | def check_version(version1: str, version2: str) -> VersionCheck:
  function check_version_agains_versionlist (line 73) | def check_version_agains_versionlist(my_version: str, all_versions: list...
  function check_version_in (line 167) | def check_version_in(version: str, versions: str) -> bool:

FILE: boefjes/boefjes/plugins/kat_snyk/main.py
  function run (line 9) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_snyk/normalize.py
  function run (line 13) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_snyk_finding_types/main.py
  function run (line 8) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_snyk_finding_types/normalize.py
  function get_risk_level (line 20) | def get_risk_level(severity_score):
  function run (line 27) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_ssl_certificates/main.py
  function run (line 4) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_ssl_certificates/normalize.py
  function find_between (line 28) | def find_between(s: str, first: str, last: str) -> str:
  function run (line 37) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:
  function read_certificates (line 94) | def read_certificates(

FILE: boefjes/boefjes/plugins/kat_ssl_scan/main.py
  function run (line 7) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_ssl_scan/normalize.py
  function run (line 10) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_testssl_sh_ciphers/main.py
  function run (line 22) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_testssl_sh_ciphers/normalize.py
  function parse_cipher (line 10) | def parse_cipher(cipher: dict) -> dict[str, Any] | None:
  function run (line 42) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_wappalyzer/normalize.py
  function run (line 37) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:
  function analyze_script_src_in_html (line 95) | def analyze_script_src_in_html(har: HarWrapper, fingerprint: schemas.Fin...

FILE: boefjes/boefjes/plugins/kat_wappalyzer/utils.py
  function replace_cpe_version (line 5) | def replace_cpe_version(cpe: str, version: str) -> str:

FILE: boefjes/boefjes/plugins/kat_webpage_analysis/check_images/normalize.py
  function run (line 13) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_webpage_analysis/find_images_in_html/normalize.py
  function run (line 12) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_webpage_analysis/har/httpx.py
  function create_har_object (line 14) | def create_har_object(response: httpx.Response) -> dict:
  function map_request (line 34) | def map_request(request: httpx.Request) -> dict:
  function map_response (line 63) | def map_response(response: httpx.Response) -> dict:
  function map_cookie (line 81) | def map_cookie(cookie: Cookie) -> dict:
  function _name_value_pairs (line 94) | def _name_value_pairs(iterable: Iterable) -> Sequence[dict]:

FILE: boefjes/boefjes/plugins/kat_webpage_analysis/har/requests.py
  function create_har_object (line 15) | def create_har_object(response: requests.Response) -> dict:
  function map_request (line 35) | def map_request(request: requests.PreparedRequest) -> dict:
  function map_response (line 64) | def map_response(response: requests.Response) -> dict:
  function map_cookie (line 82) | def map_cookie(cookie: Cookie) -> dict:
  function _name_value_pairs (line 95) | def _name_value_pairs(iterable: Iterable) -> Sequence[dict]:

FILE: boefjes/boefjes/plugins/kat_webpage_analysis/headers/normalize.py
  function run (line 9) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/plugins/kat_webpage_analysis/main.py
  function run (line 17) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:
  function do_request (line 71) | def do_request(hostname: str, session: Session, uri: str, useragent: str):
  function get_uri (line 79) | def get_uri(input_: dict) -> str:

FILE: boefjes/boefjes/plugins/kat_webpage_capture/main.py
  class WebpageCaptureException (line 7) | class WebpageCaptureException(Exception):
    method __init__ (line 10) | def __init__(self, message: str, container_log: str):
    method __str__ (line 14) | def __str__(self) -> str:
  function build_playwright_command (line 18) | def build_playwright_command(webpage: str, browser: str, tmp_path: str) ...
  function run_playwright (line 35) | def run_playwright(webpage: str, browser: str) -> tuple[bytes, bytes, by...
  function run (line 55) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_wpscan/main.py
  function run (line 7) | def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:

FILE: boefjes/boefjes/plugins/kat_wpscan/normalize.py
  function run (line 11) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/boefjes/sql/config_storage.py
  class SQLConfigStorage (line 19) | class SQLConfigStorage(SessionMixin, ConfigStorage):
    method __init__ (line 20) | def __init__(self, session: Session, encryption: EncryptMiddleware):
    method list_boefje_configs (line 25) | def list_boefje_configs(
    method upsert (line 80) | def upsert(
    method get_all_settings (line 125) | def get_all_settings(self, organisation_id: str, plugin_id: str) -> dict:
    method _convert_settings (line 135) | def _convert_settings(self, instance):
    method delete (line 142) | def delete(self, organisation_id: str, plugin_id: str) -> None:
    method is_enabled_by_id (line 147) | def is_enabled_by_id(self, plugin_id: str, organisation_id: str) -> bool:
    method get_enabled_boefjes (line 152) | def get_enabled_boefjes(self, organisation_id: str) -> list[str]:
    method get_enabled_normalizers (line 164) | def get_enabled_normalizers(self, organisation_id: str) -> list[str]:
    method get_disabled_boefjes (line 177) | def get_disabled_boefjes(self, organisation_id: str) -> list[str]:
    method get_disabled_normalizers (line 189) | def get_disabled_normalizers(self, organisation_id: str) -> list[str]:
    method get_states_for_organisation (line 202) | def get_states_for_organisation(self, organisation_id: str) -> dict[st...
    method _db_instance_by_id (line 222) | def _db_instance_by_id(self, organisation_id: str, plugin_id: str) -> ...
    method _to_boefje_config (line 253) | def _to_boefje_config(self, boefe_config_in_db: BoefjeConfigInDB) -> B...
  function create_config_storage (line 263) | def create_config_storage(session) -> ConfigStorage:
  function get_config_storage (line 268) | def get_config_storage() -> Iterator[ConfigStorage]:
  function create_encrypter (line 272) | def create_encrypter():

FILE: boefjes/boefjes/sql/db.py
  function get_engine (line 19) | def get_engine() -> Engine:
  function session_managed_iterator (line 37) | def session_managed_iterator(service_factory: Callable[[Session], Any]) ...
  class ObjectNotFoundException (line 54) | class ObjectNotFoundException(Exception):
    method __init__ (line 55) | def __init__(self, cls: type | UnionType, **kwargs):

FILE: boefjes/boefjes/sql/db_models.py
  class ScanLevel (line 10) | class ScanLevel(Enum):
  class RunOnDB (line 18) | class RunOnDB(Enum):
    method from_run_ons (line 24) | def from_run_ons(cls, run_ons: list[RunOn] | None):
    method to_run_ons (line 38) | def to_run_ons(self) -> list[RunOn]:
  class OrganisationInDB (line 48) | class OrganisationInDB(SQL_BASE):
  class BoefjeConfigInDB (line 57) | class BoefjeConfigInDB(SQL_BASE):
  class NormalizerConfigInDB (line 73) | class NormalizerConfigInDB(SQL_BASE):
  class BoefjeInDB (line 89) | class BoefjeInDB(SQL_BASE):
  class NormalizerInDB (line 117) | class NormalizerInDB(SQL_BASE):

FILE: boefjes/boefjes/sql/organisation_storage.py
  class SQLOrganisationStorage (line 16) | class SQLOrganisationStorage(SessionMixin, OrganisationStorage):
    method __init__ (line 17) | def __init__(self, session: Session, app_settings: Settings):
    method get_by_id (line 22) | def get_by_id(self, organisation_id: str) -> Organisation:
    method get_all (line 27) | def get_all(self) -> dict[str, Organisation]:
    method create (line 32) | def create(self, organisation: Organisation) -> None:
    method update (line 38) | def update(self, organisation: Organisation) -> None:
    method delete_by_id (line 43) | def delete_by_id(self, organisation_id: str) -> None:
    method _db_instance_by_id (line 48) | def _db_instance_by_id(self, organisation_id: str) -> OrganisationInDB:
    method to_organisation_in_db (line 59) | def to_organisation_in_db(organisation: Organisation) -> OrganisationI...
    method to_organisation (line 63) | def to_organisation(organisation_in_db: OrganisationInDB) -> Organisat...
  function create_organisation_storage (line 69) | def create_organisation_storage(session: Session) -> SQLOrganisationStor...
  function get_organisations_store (line 73) | def get_organisations_store() -> Iterator[OrganisationStorage]:

FILE: boefjes/boefjes/sql/plugin_storage.py
  class SQLPluginStorage (line 16) | class SQLPluginStorage(SessionMixin, PluginStorage):
    method __init__ (line 17) | def __init__(self, session: Session, app_settings: Settings):
    method get_all (line 22) | def get_all(self) -> list[PluginType]:
    method boefje_by_id (line 27) | def boefje_by_id(self, boefje_id: str) -> Boefje:
    method normalizer_by_id (line 32) | def normalizer_by_id(self, normalizer_id: str) -> Normalizer:
    method create_boefje (line 37) | def create_boefje(self, boefje: Boefje) -> None:
    method update_boefje (line 43) | def update_boefje(self, boefje_id: str, data: dict) -> None:
    method create_normalizer (line 55) | def create_normalizer(self, normalizer: Normalizer) -> None:
    method update_normalizer (line 61) | def update_normalizer(self, normalizer_id: str, data: dict) -> None:
    method delete_boefje_by_id (line 73) | def delete_boefje_by_id(self, boefje_id: str) -> None:
    method delete_normalizer_by_id (line 78) | def delete_normalizer_by_id(self, normalizer_id: str) -> None:
    method _db_boefje_instance_by_id (line 83) | def _db_boefje_instance_by_id(self, boefje_id: str) -> BoefjeInDB:
    method _db_normalizer_instance_by_id (line 91) | def _db_normalizer_instance_by_id(self, normalizer_id: str) -> Normali...
    method to_boefje_in_db (line 100) | def to_boefje_in_db(boefje: Boefje, pk: int | None = None) -> BoefjeInDB:
    method to_normalizer_in_db (line 127) | def to_normalizer_in_db(normalizer: Normalizer, pk: int | None = None)...
    method to_boefje (line 145) | def to_boefje(boefje_in_db: BoefjeInDB) -> Boefje:
    method to_normalizer (line 167) | def to_normalizer(normalizer_in_db: NormalizerInDB) -> Normalizer:
  function create_plugin_storage (line 181) | def create_plugin_storage(session) -> SQLPluginStorage:
  function get_plugin_storage (line 185) | def get_plugin_storage() -> Iterator[PluginStorage]:

FILE: boefjes/boefjes/sql/session.py
  class SessionMixin (line 12) | class SessionMixin:
    method __init__ (line 25) | def __init__(self, session: Session):
    method __enter__ (line 30) | def __enter__(self) -> Self:
    method __exit__ (line 38) | def __exit__(self, exc_type: type[Exception], exc_value: str, exc_trac...

FILE: boefjes/boefjes/storage/interfaces.py
  class StorageError (line 7) | class StorageError(Exception):
    method __init__ (line 10) | def __init__(self, message: str):
  class IntegrityError (line 14) | class IntegrityError(StorageError):
    method __init__ (line 17) | def __init__(self, message: str):
  class UniqueViolation (line 21) | class UniqueViolation(IntegrityError):
    method __init__ (line 22) | def __init__(self, message: str):
    method _get_field_name (line 26) | def _get_field_name(self, message: str) -> str | None:
  class SettingsNotConformingToSchema (line 35) | class SettingsNotConformingToSchema(StorageError):
    method __init__ (line 36) | def __init__(self, plugin_id: str, validation_error: str):
  class NotFound (line 40) | class NotFound(StorageError):
  class OrganisationNotFound (line 44) | class OrganisationNotFound(NotFound):
    method __init__ (line 45) | def __init__(self, organisation_id: str):
  class PluginNotFound (line 49) | class PluginNotFound(NotFound):
    method __init__ (line 50) | def __init__(self, plugin_id: str):
  class PluginStateNotFound (line 54) | class PluginStateNotFound(NotFound):
    method __init__ (line 55) | def __init__(self, plugin_id: str, organisation_id: str):
  class ConfigNotFound (line 59) | class ConfigNotFound(NotFound):
    method __init__ (line 60) | def __init__(self, organisation_id: str, plugin_id: str):
  class NotAllowed (line 64) | class NotAllowed(StorageError):
  class CannotUpdateStaticPlugin (line 68) | class CannotUpdateStaticPlugin(NotAllowed):
    method __init__ (line 69) | def __init__(self, plugin_id: str):
  class DuplicatePlugin (line 73) | class DuplicatePlugin(NotAllowed):
    method __init__ (line 74) | def __init__(self, field: str | None):
  class OrganisationStorage (line 78) | class OrganisationStorage(ABC):
    method __enter__ (line 79) | def __enter__(self):
    method __exit__ (line 82) | def __exit__(self, exc_type: type[Exception], exc_value: str, exc_trac...
    method get_by_id (line 85) | def get_by_id(self, organisation_id: str) -> Organisation:
    method get_all (line 88) | def get_all(self) -> dict[str, Organisation]:
    method create (line 91) | def create(self, organisation: Organisation) -> None:
    method update (line 94) | def update(self, organisation: Organisation) -> None:
    method delete_by_id (line 97) | def delete_by_id(self, organisation_id: str) -> None:
  class PluginStorage (line 101) | class PluginStorage(ABC):
    method __enter__ (line 102) | def __enter__(self):
    method __exit__ (line 105) | def __exit__(self, exc_type: type[Exception], exc_value: str, exc_trac...
    method get_all (line 108) | def get_all(self) -> list[PluginType]:
    method boefje_by_id (line 111) | def boefje_by_id(self, boefje_id: str) -> Boefje:
    method normalizer_by_id (line 114) | def normalizer_by_id(self, normalizer_id: str) -> Normalizer:
    method create_boefje (line 117) | def create_boefje(self, boefje: Boefje) -> None:
    method create_normalizer (line 120) | def create_normalizer(self, normalizer: Normalizer) -> None:
    method update_boefje (line 123) | def update_boefje(self, boefje_id: str, data: dict) -> None:
    method update_normalizer (line 126) | def update_normalizer(self, normalizer_id: str, data: dict) -> None:
    method delete_boefje_by_id (line 129) | def delete_boefje_by_id(self, boefje_id: str) -> None:
    method delete_normalizer_by_id (line 132) | def delete_normalizer_by_id(self, normalizer_id: str) -> None:
  class ConfigStorage (line 136) | class ConfigStorage(ABC):
    method __enter__ (line 137) | def __enter__(self):
    method __exit__ (line 140) | def __exit__(self, exc_type: type[Exception], exc_value: str, exc_trac...
    method get_all_settings (line 143) | def get_all_settings(self, organisation_id: str, plugin_id: str) -> di...
    method upsert (line 146) | def upsert(
    method delete (line 151) | def delete(self, organisation_id: str, plugin_id: str) -> None:
    method is_enabled_by_id (line 154) | def is_enabled_by_id(self, plugin_id: str, organisation_id: str) -> bool:
    method get_enabled_boefjes (line 157) | def get_enabled_boefjes(self, organisation_id: str) -> list[str]:
    method get_enabled_normalizers (line 160) | def get_enabled_normalizers(self, organisation_id: str) -> list[str]:
    method get_disabled_boefjes (line 163) | def get_disabled_boefjes(self, organisation_id: str) -> list[str]:
    method get_disabled_normalizers (line 166) | def get_disabled_normalizers(self, organisation_id: str) -> list[str]:
    method get_states_for_organisation (line 169) | def get_states_for_organisation(self, organisation_id: str) -> dict[st...
    method list_boefje_configs (line 172) | def list_boefje_configs(

FILE: boefjes/boefjes/storage/memory.py
  class OrganisationStorageMemory (line 11) | class OrganisationStorageMemory(OrganisationStorage):
    method __init__ (line 12) | def __init__(self, defaults: dict[str, Organisation] | None = None):
    method get_by_id (line 15) | def get_by_id(self, organisation_id: str) -> Organisation:
    method get_all (line 18) | def get_all(self) -> dict[str, Organisation]:
    method create (line 21) | def create(self, organisation: Organisation) -> None:
    method update (line 24) | def update(self, organisation: Organisation) -> None:
    method delete_by_id (line 27) | def delete_by_id(self, organisation_id: str) -> None:
  class PluginStorageMemory (line 31) | class PluginStorageMemory(PluginStorage):
    method __init__ (line 32) | def __init__(self):
    method get_all (line 36) | def get_all(self) -> list[PluginType]:
    method boefje_by_id (line 39) | def boefje_by_id(self, boefje_id: str) -> Boefje:
    method normalizer_by_id (line 45) | def normalizer_by_id(self, normalizer_id: str) -> Normalizer:
    method create_boefje (line 51) | def create_boefje(self, boefje: Boefje) -> None:
    method create_normalizer (line 54) | def create_normalizer(self, normalizer: Normalizer) -> None:
    method update_boefje (line 57) | def update_boefje(self, boefje_id: str, data: dict) -> None:
    method update_normalizer (line 69) | def update_normalizer(self, normalizer_id: str, data: dict) -> None:
    method delete_boefje_by_id (line 81) | def delete_boefje_by_id(self, boefje_id: str) -> None:
    method delete_normalizer_by_id (line 84) | def delete_normalizer_by_id(self, normalizer_id: str) -> None:
  class ConfigStorageMemory (line 88) | class ConfigStorageMemory(ConfigStorage):
    method __init__ (line 89) | def __init__(self):
    method get_all_settings (line 93) | def get_all_settings(self, organisation_id: str, plugin_id: str) -> di...
    method upsert (line 99) | def upsert(
    method delete (line 114) | def delete(self, organisation_id: str, plugin_id: str) -> None:
    method is_enabled_by_id (line 117) | def is_enabled_by_id(self, plugin_id: str, organisation_id: str) -> bool:
    method get_enabled_boefjes (line 123) | def get_enabled_boefjes(self, organisation_id: str) -> list[str]:
    method get_enabled_normalizers (line 130) | def get_enabled_normalizers(self, organisation_id: str) -> list[str]:
    method get_disabled_boefjes (line 137) | def get_disabled_boefjes(self, organisation_id: str) -> list[str]:
    method get_disabled_normalizers (line 144) | def get_disabled_normalizers(self, organisation_id: str) -> list[str]:
    method get_states_for_organisation (line 151) | def get_states_for_organisation(self, organisation_id: str) -> dict[st...
    method list_boefje_configs (line 154) | def list_boefje_configs(

FILE: boefjes/boefjes/worker/__main__.py
  function cli (line 51) | def cli(plugins: tuple[str] | None, log_level: str, input_url: str) -> N...

FILE: boefjes/boefjes/worker/boefje_handler.py
  class TemporaryEnvironment (line 18) | class TemporaryEnvironment:
    method __init__ (line 21) | def __init__(self, additional_environment: dict):
    method __enter__ (line 25) | def __enter__(self):
    method __exit__ (line 28) | def __exit__(self, exc_type, exc_val, exc_tb):
  function _copy_raw_files (line 33) | def _copy_raw_files(
  class LocalBoefjeHandler (line 48) | class LocalBoefjeHandler(BoefjeHandler):
    method __init__ (line 49) | def __init__(self, local_repository: LocalPluginRepository, boefje_sto...
    method handle (line 53) | def handle(self, task: Task) -> tuple[BoefjeMeta, BoefjeOutput] | None...
    method copy_raw_files (line 134) | def copy_raw_files(

FILE: boefjes/boefjes/worker/client.py
  class BoefjeAPIClient (line 12) | class BoefjeAPIClient(SchedulerClientInterface, BoefjeStorageInterface):
    method __init__ (line 13) | def __init__(
    method _verify_response (line 25) | def _verify_response(response: Response) -> None:
    method pop_items (line 28) | def pop_items(
    method push_item (line 51) | def push_item(self, p_item: Task) -> None:
    method patch_task (line 57) | def patch_task(self, task_id: uuid.UUID, status: TaskStatus) -> None:
    method get_task (line 61) | def get_task(self, task_id: uuid.UUID, hydrate: bool = True) -> Task:
    method save_output (line 69) | def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOu...

FILE: boefjes/boefjes/worker/interfaces.py
  class JobRuntimeError (line 12) | class JobRuntimeError(RuntimeError):
  class Queue (line 16) | class Queue(BaseModel):
  class TaskStatus (line 21) | class TaskStatus(Enum):
  class Task (line 33) | class Task(BaseModel):
  class TaskPop (line 47) | class TaskPop(BaseModel):
  class StatusEnum (line 51) | class StatusEnum(str, Enum):
  class File (line 56) | class File(BaseModel):
  class BoefjeInput (line 62) | class BoefjeInput(BaseModel):
  class BoefjeOutput (line 68) | class BoefjeOutput(BaseModel):
  class WorkerManager (line 73) | class WorkerManager:
    class Queue (line 74) | class Queue(Enum):
    method run (line 78) | def run(self, queue: Queue) -> None:
  class BoefjeHandler (line 82) | class BoefjeHandler:
    method handle (line 83) | def handle(self, task: Task) -> tuple[BoefjeMeta, BoefjeOutput] | None...
    method copy_raw_files (line 94) | def copy_raw_files(
  class NormalizerHandler (line 100) | class NormalizerHandler:
    method handle (line 101) | def handle(self, task: Task) -> None:
  class PaginatedTasksResponse (line 105) | class PaginatedTasksResponse(BaseModel):
  class SchedulerClientInterface (line 112) | class SchedulerClientInterface:
    method pop_items (line 113) | def pop_items(
    method patch_task (line 118) | def patch_task(self, task_id: uuid.UUID, status: TaskStatus) -> None:
    method get_task (line 121) | def get_task(self, task_id: uuid.UUID, hydrate: bool = True) -> Task:
    method push_item (line 124) | def push_item(self, p_item: Task) -> None:
  class BoefjeStorageInterface (line 128) | class BoefjeStorageInterface:
    method save_output (line 129) | def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOu...

FILE: boefjes/boefjes/worker/job_models.py
  class JobException (line 9) | class JobException(Exception):
    method __init__ (line 12) | def __init__(self, message: str):
  class Job (line 16) | class Job(BaseModel):
    method runtime (line 22) | def runtime(self) -> timedelta | None:
  class Boefje (line 29) | class Boefje(BaseModel):
  class BoefjeMeta (line 37) | class BoefjeMeta(Job):
  class RawDataMeta (line 46) | class RawDataMeta(BaseModel):
  class Normalizer (line 52) | class Normalizer(BaseModel):
  class NormalizerMeta (line 59) | class NormalizerMeta(Job):
  class ObservationsWithoutInputOOI (line 64) | class ObservationsWithoutInputOOI(JobException):
    method __init__ (line 65) | def __init__(self, normalizer_meta: NormalizerMeta):
  class InvalidReturnValueNormalizer (line 74) | class InvalidReturnValueNormalizer(JobException):

FILE: boefjes/boefjes/worker/manager.py
  class SchedulerWorkerManager (line 22) | class SchedulerWorkerManager(WorkerManager):
    method __init__ (line 23) | def __init__(
    method run (line 46) | def run(self, queue_type: WorkerManager.Queue) -> None:
    method _fill_queue (line 82) | def _fill_queue(self, queue_type: WorkerManager.Queue) -> None:
    method _replace_broken_workers (line 127) | def _replace_broken_workers(self) -> None:
    method _cleanup_pending_worker_task (line 164) | def _cleanup_pending_worker_task(self, worker: BaseProcess) -> None:
    method _worker_args (line 183) | def _worker_args(self) -> tuple:
    method exit (line 186) | def exit(self, signum: int | None = None) -> None:
  function _format_exit_code (line 224) | def _format_exit_code(exitcode: int | None) -> str:
  function _start_working (line 231) | def _start_working(

FILE: boefjes/boefjes/worker/models.py
  class RunOn (line 16) | class RunOn(Enum):
    method __lt__ (line 20) | def __lt__(self, other):
  class Organisation (line 24) | class Organisation(BaseModel):
  class Plugin (line 30) | class Plugin(BaseModel):
    method __str__ (line 39) | def __str__(self):
  class Boefje (line 43) | class Boefje(Plugin):
    method json_schema_valid (line 59) | def json_schema_valid(cls, schema: dict) -> dict:
    method cron_valid (line 70) | def cron_valid(cls, cron: str | None) -> str | None:
    class Config (line 76) | class Config:
  class BoefjeConfig (line 80) | class BoefjeConfig(BaseModel):
  class Normalizer (line 91) | class Normalizer(Plugin):
  class Bit (line 98) | class Bit(Plugin):
  class PaginationParameters (line 109) | class PaginationParameters(BaseModel):
  class FilterParameters (line 114) | class FilterParameters(BaseModel):

FILE: boefjes/boefjes/worker/oci_adapter.py
  class CallbackStorageClient (line 16) | class CallbackStorageClient(BoefjeStorageInterface):
    method __init__ (line 17) | def __init__(self, base_url: str, callback_url: str, outgoing_request_...
    method save_output (line 21) | def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOu...
  function run_with_callback (line 28) | def run_with_callback(input_url: str):

FILE: boefjes/boefjes/worker/repository.py
  class ModuleException (line 27) | class ModuleException(Exception):
  class Runnable (line 31) | class Runnable(Protocol):
    method run (line 32) | def run(self, *args, **kwargs):
  class BoefjeResource (line 36) | class BoefjeResource:
    method __init__ (line 39) | def __init__(self, path: Path, package: str, path_hash: str):
  class NormalizerResource (line 61) | class NormalizerResource:
    method __init__ (line 64) | def __init__(self, path: Path, package: str):
  class LocalPluginRepository (line 71) | class LocalPluginRepository:
    method __init__ (line 72) | def __init__(self, path: Path):
    method get_all (line 75) | def get_all(self) -> list[PluginType]:
    method by_id (line 80) | def by_id(self, plugin_id: str) -> BoefjeResource | NormalizerResource:
    method by_image (line 93) | def by_image(self, image: str) -> BoefjeResource:
    method by_name (line 102) | def by_name(self, plugin_name: str) -> BoefjeResource | NormalizerReso...
    method schema (line 115) | def schema(self, id_: str) -> dict | None:
    method cover_path (line 129) | def cover_path(self, plugin_id: str) -> Path:
    method default_cover_path (line 150) | def default_cover_path(self) -> Path:
    method description_path (line 153) | def description_path(self, id_: str) -> Path | None:
    method resolve_boefjes (line 161) | def resolve_boefjes(self) -> dict[str, BoefjeResource]:
    method resolve_normalizers (line 164) | def resolve_normalizers(self) -> dict[str, NormalizerResource]:
  function _cached_resolve_boefjes (line 169) | def _cached_resolve_boefjes(path: Path) -> dict[str, BoefjeResource]:
  function _cached_resolve_normalizers (line 183) | def _cached_resolve_normalizers(path: Path) -> dict[str, NormalizerResou...
  function _find_packages_in_path_containing_files (line 198) | def _find_packages_in_path_containing_files(path: Path, required_files: ...
  function create_relative_import_statement_from_cwd (line 219) | def create_relative_import_statement_from_cwd(package_dir: Path) -> str:
  function get_boefje_resource (line 226) | def get_boefje_resource(path: Path, package: str, path_hash: str):
  function get_normalizer_resource (line 234) | def get_normalizer_resource(path: Path, package: str, path_hash: str):
  function get_local_repository (line 241) | def get_local_repository():
  function get_runnable_module_from_package (line 245) | def get_runnable_module_from_package(package: str, module_file: str, *, ...
  function hash_path (line 262) | def hash_path(path: Path) -> str:
  function _default_mime_types (line 278) | def _default_mime_types(boefje: Boefje) -> set:

FILE: boefjes/export_migrations/0001_add_katalogus_models.sql
  type organisation (line 2) | CREATE TABLE organisation (
  type repository (line 9) | CREATE TABLE repository (
  type organisation_repository (line 17) | CREATE TABLE organisation_repository (
  type setting (line 24) | CREATE TABLE setting (
  type plugin_state (line 33) | CREATE TABLE plugin_state (

FILE: boefjes/export_migrations/0005_cd34fdfafdaf_json_settings_for_settings_table.sql
  type settings (line 4) | CREATE TABLE settings (

FILE: boefjes/export_migrations/0007_6f99834a4a5a_introduce_boefje_and_normalizer_models.sql
  type boefje (line 3) | CREATE TABLE boefje (
  type normalizer (line 20) | CREATE TABLE normalizer (

FILE: boefjes/export_migrations/0008_f9de6eb7824b_introduce_boefjeconfig_model.sql
  type boefje_config (line 1) | CREATE TABLE boefje_config (
  type normalizer_config (line 15) | CREATE TABLE normalizer_config (

FILE: boefjes/tests/conftest.py
  class MockSchedulerClient (line 62) | class MockSchedulerClient(SchedulerClientInterface):
    method __init__ (line 63) | def __init__(
    method pop_items (line 86) | def pop_items(
    method patch_task (line 111) | def patch_task(self, task_id: UUID, status: TaskStatus) -> None:
    method get_all_patched_tasks (line 119) | def get_all_patched_tasks(self) -> list[tuple[str, ...]]:
    method get_task (line 123) | def get_task(self, task_id: UUID, hydrate: bool = True) -> Task:
    method _task_from_id (line 126) | def _task_from_id(self, task_id: UUID):
    method push_item (line 129) | def push_item(self, p_item: Task) -> None:
  class MockBytesAPIClient (line 133) | class MockBytesAPIClient(BoefjeStorageInterface):
    method __init__ (line 134) | def __init__(self):
    method save_output (line 137) | def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOu...
    method save_boefje_meta (line 142) | def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:
    method save_raws (line 145) | def save_raws(self, boefje_meta_id: uuid.UUID, boefje_output: BoefjeOu...
    method get_all (line 150) | def get_all(self) -> list[BoefjeMeta | NormalizerMeta]:
  class MockHandler (line 154) | class MockHandler(BoefjeHandler, NormalizerHandler):
    method __init__ (line 155) | def __init__(self, exception=Exception):
    method handle (line 161) | def handle(self, task: Task) -> tuple[BoefjeMeta, BoefjeOutput] | None...
    method copy_raw_files (line 178) | def copy_raw_files(
    method get_all (line 188) | def get_all(self) -> list[Task]:
  function clear_caches (line 193) | def clear_caches():
  function item_handler (line 201) | def item_handler(tmp_path: Path):
  function mock_boefje_handler (line 206) | def mock_boefje_handler(mock_local_repository: LocalPluginRepository, mo...
  function manager (line 211) | def manager(item_handler: MockHandler, tmp_path: Path) -> SchedulerWorke...
  function api (line 228) | def api(tmp_path):
  function session (line 235) | def session():
  function organisation_storage (line 247) | def organisation_storage(session):
  function config_storage (line 252) | def config_storage(session):
  function plugin_storage (line 257) | def plugin_storage(session):
  function local_repository (line 262) | def local_repository():
  function mock_local_repository (line 267) | def mock_local_repository():
  function normalizer_runner (line 272) | def normalizer_runner(local_repository: LocalPluginRepository):
  function mock_normalizer_runner (line 277) | def mock_normalizer_runner(mock_local_repository: LocalPluginRepository):
  function plugin_service (line 282) | def plugin_service(plugin_storage, config_storage, local_repository):
  function test_organisation (line 287) | def test_organisation():
  function second_test_organisation (line 292) | def second_test_organisation():
  function mock_plugin_service (line 297) | def mock_plugin_service(mock_local_repository, test_organisation) -> Plu...
  function organisation (line 305) | def organisation(organisation_storage, test_organisation) -> Organisation:
  function second_organisation (line 313) | def second_organisation(organisation_storage, second_test_organisation) ...
  function unit_test_client (line 321) | def unit_test_client(mock_plugin_service) -> TestClient:
  function test_client (line 342) | def test_client() -> TestClient:
  function octopoes_api_connector (line 347) | def octopoes_api_connector(organisation) -> OctopoesAPIConnector:
  function bytes_client (line 355) | def bytes_client(request) -> BytesAPIClient:
  function valid_time (line 360) | def valid_time():
  function seed_system (line 364) | def seed_system(

FILE: boefjes/tests/integration/test_api.py
  function test_get_local_plugin (line 10) | def test_get_local_plugin(test_client, organisation):
  function test_create_org (line 18) | def test_create_org(test_client):
  function test_filter_plugins (line 25) | def test_filter_plugins(test_client, organisation):
  function test_cannot_add_plugin_reserved_id (line 70) | def test_cannot_add_plugin_reserved_id(test_client, organisation):
  function test_add_boefje (line 82) | def test_add_boefje(test_client, organisation):
  function test_enable_boefje (line 101) | def test_enable_boefje(test_client, organisation, second_organisation):
  function test_run_on (line 111) | def test_run_on(test_client, organisation, second_organisation):
  function test_cannot_add_static_plugin_with_duplicate_name (line 127) | def test_cannot_add_static_plugin_with_duplicate_name(test_client, organ...
  function test_cannot_add_plugin_with_duplicate_name (line 138) | def test_cannot_add_plugin_with_duplicate_name(test_client, organisation):
  function test_delete_boefje (line 157) | def test_delete_boefje(test_client, organisation):
  function test_add_normalizer (line 168) | def test_add_normalizer(test_client, organisation):
  function test_delete_normalizer (line 180) | def test_delete_normalizer(test_client, organisation):
  function test_update_plugins (line 191) | def test_update_plugins(test_client, organisation, second_organisation):
  function test_cannot_create_boefje_with_invalid_schema (line 216) | def test_cannot_create_boefje_with_invalid_schema(test_client, organisat...
  function test_schema_is_taken_from_disk (line 224) | def test_schema_is_taken_from_disk(test_client, organisation, session):
  function test_cannot_set_invalid_cron (line 234) | def test_cannot_set_invalid_cron(test_client, organisation):
  function test_update_boefje_schema (line 247) | def test_update_boefje_schema(test_client, organisation):
  function test_cannot_update_static_plugins (line 279) | def test_cannot_update_static_plugins(test_client, organisation):
  function test_basic_settings_api (line 301) | def test_basic_settings_api(test_client, organisation):
  function test_clone_settings_and_config_api_shows_both (line 322) | def test_clone_settings_and_config_api_shows_both(test_client, organisat...

FILE: boefjes/tests/integration/test_bench.py
  function test_migration (line 21) | def test_migration(
  function test_plugins_bench (line 122) | def test_plugins_bench(plugin_service, organisation):

FILE: boefjes/tests/integration/test_get_environment.py
  function test_environment_builds_up_correctly (line 10) | def test_environment_builds_up_correctly(plugin_service: PluginService, ...

FILE: boefjes/tests/integration/test_json_settings_encryption_migration.py
  function migration_197672984df0 (line 16) | def migration_197672984df0() -> Session:
  function test_setting_to_settings_json (line 34) | def test_setting_to_settings_json(migration_197672984df0):
  function _collect_entries (line 69) | def _collect_entries(encrypter: NaclBoxMiddleware):

FILE: boefjes/tests/integration/test_migration_add_schema_field.py
  function migration_f9de6eb7824b (line 14) | def migration_f9de6eb7824b(local_repository) -> Session:
  function test_fail_on_wrong_plugin_ids (line 59) | def test_fail_on_wrong_plugin_ids(migration_f9de6eb7824b):

FILE: boefjes/tests/integration/test_remove_repository_migration.py
  function migration_cd34fdfafdaf (line 14) | def migration_cd34fdfafdaf() -> Session:
  function test_fail_on_non_unique (line 57) | def test_fail_on_non_unique(migration_cd34fdfafdaf):
  function test_downgrade (line 86) | def test_downgrade(migration_cd34fdfafdaf):

FILE: boefjes/tests/integration/test_settings_to_boefje_config_migration.py
  function migration_6f99834a4a5a (line 15) | def migration_6f99834a4a5a() -> Session:
  function test_fail_on_wrong_plugin_ids (line 53) | def test_fail_on_wrong_plugin_ids(migration_6f99834a4a5a):
  function test_downgrade (line 104) | def test_downgrade(migration_6f99834a4a5a):

FILE: boefjes/tests/integration/test_sql_repositories.py
  function test_organisation_storage (line 12) | def test_organisation_storage(organisation_storage):
  function test_settings_storage (line 35) | def test_settings_storage(plugin_storage, organisation_storage, config_s...
  function test_settings_storage_values_respect_field_limits (line 113) | def test_settings_storage_values_respect_field_limits(plugin_storage, or...
  function test_plugin_enabled_storage (line 149) | def test_plugin_enabled_storage(organisation_storage, plugin_storage, co...
  function test_bare_boefje_storage (line 190) | def test_bare_boefje_storage(plugin_storage):
  function test_rich_boefje_storage (line 238) | def test_rich_boefje_storage(plugin_storage):
  function test_bare_normalizer_storage (line 268) | def test_bare_normalizer_storage(plugin_storage):
  function test_rich_normalizer_storage (line 295) | def test_rich_normalizer_storage(plugin_storage):
  function test_plugin_storage (line 322) | def test_plugin_storage(plugin_storage):

FILE: boefjes/tests/katalogus/test_organisation_api.py
  function test_list (line 1) | def test_list(unit_test_client):
  function test_get_organisation (line 6) | def test_get_organisation(unit_test_client):
  function test_non_existing_organisation (line 11) | def test_non_existing_organisation(unit_test_client):
  function test_add_organisation (line 17) | def test_add_organisation(unit_test_client):
  function test_delete_organisation (line 26) | def test_delete_organisation(unit_test_client):

FILE: boefjes/tests/katalogus/test_plugin_service.py
  function test_get_plugins (line 6) | def test_get_plugins(mock_plugin_service, test_organisation):
  function test_get_plugin_by_id (line 22) | def test_get_plugin_by_id(mock_plugin_service, test_organisation):
  function test_update_by_id (line 29) | def test_update_by_id(mock_plugin_service, test_organisation):
  function test_update_by_id_bad_schema (line 35) | def test_update_by_id_bad_schema(mock_plugin_service, test_organisation):
  function test_get_schema (line 48) | def test_get_schema(mock_plugin_service):
  function test_removing_mandatory_setting_does_not_disable_plugin_anymore (line 61) | def test_removing_mandatory_setting_does_not_disable_plugin_anymore(mock...
  function test_adding_integer_settings_within_given_constraints (line 76) | def test_adding_integer_settings_within_given_constraints(mock_plugin_se...
  function test_clone_one_setting (line 92) | def test_clone_one_setting(mock_plugin_service, test_organisation):
  function test_clone_many_settings (line 118) | def test_clone_many_settings(mock_plugin_service, test_organisation):

FILE: boefjes/tests/katalogus/test_plugins_api.py
  function test_list (line 1) | def test_list(unit_test_client):
  function test_list_filter_by_type (line 9) | def test_list_filter_by_type(unit_test_client):
  function test_list_filter_by_state (line 15) | def test_list_filter_by_state(unit_test_client):
  function test_list_filter_by_id (line 22) | def test_list_filter_by_id(unit_test_client):
  function test_list_pagination (line 28) | def test_list_pagination(unit_test_client):
  function test_list_plugins (line 34) | def test_list_plugins(unit_test_client):
  function test_get_plugin (line 42) | def test_get_plugin(unit_test_client):
  function test_non_existing_plugin (line 49) | def test_non_existing_plugin(unit_test_client):
  function test_default_enabled_property_list (line 54) | def test_default_enabled_property_list(unit_test_client):
  function test_patching_enabled_state (line 60) | def test_patching_enabled_state(unit_test_client):
  function test_patching_enabled_state_non_existing_org (line 75) | def test_patching_enabled_state_non_existing_org(unit_test_client):

FILE: boefjes/tests/katalogus/test_settings.py
  function test_encode_decode (line 8) | def test_encode_decode():

FILE: boefjes/tests/loading.py
  function get_dummy_data (line 10) | def get_dummy_data(filename: str) -> bytes:
  function get_task (line 15) | def get_task(
  function get_boefje_meta (line 32) | def get_boefje_meta(
  function get_normalizer_meta (line 48) | def get_normalizer_meta(
  function get_raw_data_meta (line 60) | def get_raw_data_meta(

FILE: boefjes/tests/modules/dummy_bad_normalizer_dict_structure/normalize.py
  function run (line 6) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/tests/modules/dummy_bad_normalizer_return_type/normalize.py
  function run (line 6) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/tests/modules/dummy_boefje/main.py
  function run (line 4) | def run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:

FILE: boefjes/tests/modules/dummy_boefje_environment/main.py
  function run (line 6) | def run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:

FILE: boefjes/tests/modules/dummy_boefje_environment_with_pycache/main.py
  function run (line 6) | def run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:

FILE: boefjes/tests/modules/dummy_boefje_invalid_signature/main.py
  function run (line 4) | def run(boefje_meta: str) -> tuple[str, int]:

FILE: boefjes/tests/modules/dummy_boefje_runtime_exception/main.py
  function run (line 4) | def run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:

FILE: boefjes/tests/modules/dummy_normalizer/normalize.py
  function run (line 7) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/tests/modules/kat_test/kat_test_2/kat_test_3/normalize.py
  function run (line 7) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/tests/modules/kat_test/kat_test_2/main.py
  function run (line 4) | def run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:

FILE: boefjes/tests/modules/kat_test/main.py
  function run (line 4) | def run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:

FILE: boefjes/tests/modules/kat_test/normalize.py
  function run (line 7) | def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:

FILE: boefjes/tests/plugins/test_adr_validator.py
  function test_no_findings (line 5) | def test_no_findings(normalizer_runner):
  function test_with_findings (line 24) | def test_with_findings(normalizer_runner):

FILE: boefjes/tests/plugins/test_answer_parser.py
  function test_config_yielded (line 8) | def test_config_yielded(normalizer_runner):

FILE: boefjes/tests/plugins/test_bodyimage.py
  function test_website_analysis (line 10) | def test_website_analysis(local_repository, mocker):
  function test_website_analysis_for_image (line 30) | def test_website_analysis_for_image(mocker):
  function test_body_image_normalizer (line 47) | def test_body_image_normalizer(normalizer_runner):
  function test_body_normalizer (line 72) | def test_body_normalizer(normalizer_runner):

FILE: boefjes/tests/plugins/test_calvin.py
  function test_parse_user_changed (line 6) | def test_parse_user_changed(normalizer_runner):
  function test_parse_admin_login_failure (line 62) | def test_parse_admin_login_failure(normalizer_runner):
  function test_parse_user_login_failure (line 95) | def test_parse_user_login_failure(normalizer_runner):

FILE: boefjes/tests/plugins/test_cve-2023-35078.py
  function test_vulnerable_version_11_8 (line 6) | def test_vulnerable_version_11_8():
  function test_vulnerable_version_11_9 (line 12) | def test_vulnerable_version_11_9():
  function test_vulnerable_version_11_10 (line 18) | def test_vulnerable_version_11_10():
  function test_patched_version_11_9 (line 24) | def test_patched_version_11_9():
  function test_equal_to_patched_version_11_9 (line 30) | def test_equal_to_patched_version_11_9():
  function test_cve_2023_35078_vulnerable (line 36) | def test_cve_2023_35078_vulnerable(mocker):
  function test_cve_2023_35078_not_vulnerable (line 42) | def test_cve_2023_35078_not_vulnerable(mocker):

FILE: boefjes/tests/plugins/test_cve-2024-6387.py
  function test_is_vulnerable (line 4) | def test_is_vulnerable():

FILE: boefjes/tests/plugins/test_cve_finding_types.py
  function test_cve_with_cvss (line 8) | def test_cve_with_cvss():
  function test_cve_with_cvss2 (line 29) | def test_cve_with_cvss2():
  function test_cve_without_cvss (line 51) | def test_cve_without_cvss():

FILE: boefjes/tests/plugins/test_dns.py
  function test_dns_normalizer (line 30) | def test_dns_normalizer(normalizer_runner):
  function test_dns_normalizer_cname (line 124) | def test_dns_normalizer_cname(normalizer_runner):
  function test_parse_record_null_mx_record (line 193) | def test_parse_record_null_mx_record(normalizer_runner):
  function test_parse_cname_soa (line 229) | def test_parse_cname_soa(normalizer_runner):
  function test_find_parent_dns_zone (line 317) | def test_find_parent_dns_zone(normalizer_runner):
  function test_exception_raised_no_input_ooi (line 369) | def test_exception_raised_no_input_ooi(normalizer_runner):

FILE: boefjes/tests/plugins/test_dnssec.py
  function test_dnssec_unsigned (line 7) | def test_dnssec_unsigned():
  function test_dnssec_invalid (line 14) | def test_dnssec_invalid():
  function test_dnssec_valid (line 21) | def test_dnssec_valid():
  function test_dnssec_status_line_not_last_line (line 28) | def test_dnssec_status_line_not_last_line():

FILE: boefjes/tests/plugins/test_fierce.py
  function test_fierce (line 11) | def test_fierce():

FILE: boefjes/tests/plugins/test_generic_finding_normalizer.py
  function test_single (line 7) | def test_single():
  function test_multiple (line 24) | def test_multiple():

FILE: boefjes/tests/plugins/test_leakix.py
  function test_output (line 12) | def test_output():
  function _get_hostname_input_ooi (line 30) | def _get_hostname_input_ooi():
  function test_strict_mode_filters_hostname_subdomains (line 36) | def test_strict_mode_filters_hostname_subdomains():
  function test_permissive_mode_keeps_all_hostname_results (line 56) | def test_permissive_mode_keeps_all_hostname_results():

FILE: boefjes/tests/plugins/test_manual.py
  function test_parse_manual_declarations (line 44) | def test_parse_manual_declarations(normalizer_runner):
  function test_parse_manual_hostname_csv (line 67) | def test_parse_manual_hostname_csv(normalizer_runner):
  function test_parse_manual_ip_csv (line 97) | def test_parse_manual_ip_csv(normalizer_runner):
  function test_parse_url_csv (line 122) | def test_parse_url_csv(normalizer_runner):
  function check_network_created (line 149) | def check_network_created(

FILE: boefjes/tests/plugins/test_nmap.py
  function test_normalizer (line 12) | def test_normalizer():
  function get_pattern (line 28) | def get_pattern():
  function test_single_port_pattern (line 36) | def test_single_port_pattern(local_repository):
  function test_bad_single_port_pattern (line 43) | def test_bad_single_port_pattern(local_repository):
  function test_multi_ports_pattern (line 51) | def test_multi_ports_pattern(local_repository):
  function test_port_range_pattern (line 58) | def test_port_range_pattern(local_repository):
  function test_combined (line 65) | def test_combined(local_repository):
  function test_badly_combined (line 72) | def test_badly_combined(local_repository):

FILE: boefjes/tests/plugins/test_rdns.py
  function test_rdns_nxdomain (line 10) | def test_rdns_nxdomain():
  function test_rdns_answer_1 (line 37) | def test_rdns_answer_1():
  function test_rdns_answer_2 (line 43) | def test_rdns_answer_2():

FILE: boefjes/tests/plugins/test_report_data.py
  function test_report_data (line 8) | def test_report_data(normalizer_runner):

FILE: boefjes/tests/plugins/test_scan_profiles.py
  function test_normalizer_can_yield_scan_profiles (line 21) | def test_normalizer_can_yield_scan_profiles(normalizer_runner):
  function test_job_handler_respects_whitelist (line 45) | def test_job_handler_respects_whitelist(normalizer_runner, mocker):

FILE: boefjes/tests/plugins/test_security_txt.py
  function test_security_txt_same_website (line 27) | def test_security_txt_same_website():
  function test_security_txt_different_website (line 46) | def test_security_txt_different_website():

FILE: boefjes/tests/plugins/test_snyk.py
  function test_snyk_no_findings (line 14) | def test_snyk_no_findings():
  function test_snyk_findings (line 18) | def test_snyk_findings():
  function test_snyk_html_parser (line 57) | def test_snyk_html_parser(mocker):

FILE: boefjes/tests/plugins/test_sslcertificate_normalizer.py
  function test_ssl_certificates_normalizer (line 22) | def test_ssl_certificates_normalizer():

FILE: boefjes/tests/plugins/test_testssl_sh.py
  function test_cipherless_service (line 18) | def test_cipherless_service():
  function test_ciphered_service (line 27) | def test_ciphered_service():

FILE: boefjes/tests/plugins/test_wappalyzer_normalizer.py
  function test_page_analyzer_normalizer (line 5) | def test_page_analyzer_normalizer(normalizer_runner):

FILE: boefjes/tests/test_api.py
  function _mocked_scheduler_client (line 12) | def _mocked_scheduler_client(tmp_path: Path):
  function test_healthz (line 20) | def test_healthz(api):
  function test_boefje_input_running (line 26) | def test_boefje_input_running(api, tmp_path):
  function test_boefje_input_not_running (line 66) | def test_boefje_input_not_running(api, tmp_path):

FILE: boefjes/tests/test_app.py
  function test_one_process (line 17) | def test_one_process(manager: SchedulerWorkerManager, item_handler: Mock...
  function test_two_processes (line 38) | def test_two_processes(manager: SchedulerWorkerManager, item_handler: Mo...
  function test_two_processes_exception (line 59) | def test_two_processes_exception(manager: SchedulerWorkerManager, item_h...
  function test_two_processes_with_exception_in_handler (line 74) | def test_two_processes_with_exception_in_handler(
  function test_two_processes_cleanup_unfinished_tasks (line 123) | def test_two_processes_cleanup_unfinished_tasks(
  function test_normalizer_queue (line 165) | def test_normalizer_queue(manager: SchedulerWorkerManager, item_handler:...
  function test_null (line 174) | def test_null(manager: SchedulerWorkerManager, tmp_path: Path, item_hand...
  function test_one_process_deduplication_turned_off (line 200) | def test_one_process_deduplication_turned_off(manager: SchedulerWorkerMa...
  function test_one_process_deduplication_of_tasks (line 231) | def test_one_process_deduplication_of_tasks(manager: SchedulerWorkerMana...
  function test_one_process_deduplication_exception_puts_duplicated_task_back_on_the_queue (line 285) | def test_one_process_deduplication_exception_puts_duplicated_task_back_o...
  function test_one_process_deduplication_duplicate_docker_boefje_as_well (line 307) | def test_one_process_deduplication_duplicate_docker_boefje_as_well(
  function test_create_manager (line 332) | def test_create_manager():

FILE: boefjes/tests/test_job_handler.py
  function test_boefje_systems_vars (line 4) | def test_boefje_systems_vars(monkeypatch):
  function test_boefje_system_vars_no_vars (line 14) | def test_boefje_system_vars_no_vars():
  function test_boefje_systems_vars_no_allowed_keys (line 22) | def test_boefje_systems_vars_no_allowed_keys(monkeypatch):

FILE: boefjes/tests/test_models.py
  function test_run_on (line 5) | def test_run_on():

FILE: boefjes/tests/test_nikto_normalizer.py
  class NiktoNormalizerTest (line 10) | class NiktoNormalizerTest(TestCase):
    method test_outdated_and_legacy_missing_header (line 11) | def test_outdated_and_legacy_missing_header(self):
    method test_unmapped_id_yields_generic_finding (line 50) | def test_unmapped_id_yields_generic_finding(self):
    method test_nikto_2_6_full_coverage (line 64) | def test_nikto_2_6_full_coverage(self):

FILE: boefjes/tests/test_tasks.py
  function test_parse_normalizer_meta_to_json (line 42) | def test_parse_normalizer_meta_to_json():
  function test_handle_boefje_with_exception (line 52) | def test_handle_boefje_with_exception(mocker):
  function test_exception_raised_unsupported_return_type_normalizer (line 95) | def test_exception_raised_unsupported_return_type_normalizer(mock_normal...
  function test_exception_raised_invalid_return_value (line 104) | def test_exception_raised_invalid_return_value(mock_normalizer_runner):
  function test_cleared_boefje_env (line 113) | def test_cleared_boefje_env(mock_boefje_handler) -> None:
  function test_correct_local_runner_hash (line 125) | def test_correct_local_runner_hash(mock_local_repository) -> None:

FILE: boefjes/tools/run_boefje.py
  function run_boefje (line 34) | def run_boefje(start_pdb, organization_code, boefje_id, input_ooi):

FILE: boefjes/tools/run_normalizer.py
  function run_normalizer (line 27) | def run_normalizer(start_pdb, normalizer_id, raw_id):

FILE: boefjes/tools/show_raw.py
  function show_raw (line 21) | def show_raw(print_json, raw_id):

FILE: boefjes/tools/upgrade_v1_17_0.py
  function upgrade (line 38) | def upgrade(organisation_repository: OrganisationStorage, valid_time: da...
  function migrate_organisation (line 75) | def migrate_organisation(
  function collect_boefjes_per_normalizer (line 162) | def collect_boefjes_per_normalizer() -> dict[str, list[Boefje]]:
  function main (line 186) | def main():

FILE: bytes/bytes/api/metrics.py
  function ignore_arguments_key (line 26) | def ignore_arguments_key(meta_repository: MetaDataRepository) -> str:
  function cached_counts_per_organization (line 31) | def cached_counts_per_organization(meta_repository: MetaDataRepository) ...
  function get_registry (line 40) | def get_registry(meta_repository: MetaDataRepository) -> CollectorRegistry:

FILE: bytes/bytes/api/models.py
  class RawResponse (line 6) | class RawResponse(BaseModel):
  class File (line 12) | class File(BaseModel):
  class StatusEnum (line 18) | class StatusEnum(str, Enum):
  class BoefjeOutput (line 23) | class BoefjeOutput(BaseModel):

FILE: bytes/bytes/api/root.py
  class ServiceHealth (line 22) | class ServiceHealth(BaseModel):
  function validation_exception_handler (line 33) | def validation_exception_handler(_: Request, exc: RequestValidationError...
  function root (line 39) | def root() -> RedirectResponse:
  function health (line 44) | def health() -> ServiceHealth:
  function metrics (line 50) | def metrics(meta_repository: MetaDataRepository = Depends(create_meta_da...
  function login (line 58) | def login(form_data: OAuth2PasswordRequestForm = Depends()) -> TokenResp...

FILE: bytes/bytes/api/router.py
  function create_boefje_meta (line 29) | def create_boefje_meta(
  function get_boefje_meta_by_id (line 45) | def get_boefje_meta_by_id(
  function get_boefje_meta (line 60) | def get_boefje_meta(
  function create_normalizer_meta (line 93) | def create_normalizer_meta(
  function get_normalizer_meta_by_id (line 109) | def get_normalizer_meta_by_id(
  function get_normalizer_metas (line 119) | def get_normalizer_metas(
  function get_normalizer_meta (line 130) | def get_normalizer_meta(
  function create_raw (line 164) | async def create_raw(
  function get_raw_by_id (line 219) | def get_raw_by_id(raw_id: UUID, meta_repository: MetaDataRepository = De...
  function get_raw_meta_by_id (line 229) | def get_raw_meta_by_id(
  function get_raw (line 241) | def get_raw(
  function get_raws (line 267) | def get_raws(
  function get_raw_count_per_mime_type (line 305) | def get_raw_count_per_mime_type(
  function ignore_arguments_key (line 326) | def ignore_arguments_key(meta_repository: MetaDataRepository, query_filt...
  function cached_counts_per_mime_type (line 335) | def cached_counts_per_mime_type(meta_repository: MetaDataRepository, que...

FILE: bytes/bytes/auth.py
  class TokenResponse (line 21) | class TokenResponse(BaseModel):
  function get_access_token (line 27) | def get_access_token(form_data: OAuth2PasswordRequestForm) -> tuple[str,...
  function authenticate_token (line 44) | def authenticate_token(token: str = Depends(oauth2_scheme)) -> str:
  function _create_access_token (line 64) | def _create_access_token(
  function _get_expire_time (line 75) | def _get_expire_time(access_token_expire_minutes: float) -> datetime:

FILE: bytes/bytes/config.py
  class BackwardsCompatibleEnvSettings (line 19) | class BackwardsCompatibleEnvSettings(EnvSettingsSource):
    method __call__ (line 35) | def __call__(self) -> dict[str, Any]:
  class Settings (line 59) | class Settings(BaseSettings):
    method settings_customise_sources (line 152) | def settings_customise_sources(
  function get_settings (line 165) | def get_settings() -> Settings:
  function has_rfc3161_provider (line 169) | def has_rfc3161_provider() -> bool:

FILE: bytes/bytes/database/db.py
  function get_engine (line 14) | def get_engine(db_uri: str, pool_size: int) -> Engine:

FILE: bytes/bytes/database/db_models.py
  class BoefjeMetaInDB (line 8) | class BoefjeMetaInDB(SQL_BASE):
  class SigningProviderInDB (line 27) | class SigningProviderInDB(SQL_BASE):
  class RawFileInDB (line 35) | class RawFileInDB(SQL_BASE):
  class NormalizerMetaInDB (line 54) | class NormalizerMetaInDB(SQL_BASE):

FILE: bytes/bytes/database/migrations/env.py
  function run_migrations_offline (line 27) | def run_migrations_offline() -> None:
  function run_migrations_online (line 48) | def run_migrations_online() -> None:

FILE: bytes/bytes/database/migrations/versions/0001_initial.py
  function upgrade (line 20) | def upgrade() -> None:
  function downgrade (line 57) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0002_drop_output_add_input.py
  function upgrade (line 20) | def upgrade() -> None:
  function downgrade (line 44) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0003_rename_module_to_normalizer_boefje.py
  function upgrade (line 18) | def upgrade() -> None:
  function downgrade (line 28) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0004_rename_boefje_name_to_boefje_id.py
  function upgrade (line 18) | def upgrade() -> None:
  function downgrade (line 24) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0005_add_secure_hash_link_boefje_meta.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 28) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0006_remove_redundant_dispatches_field.py
  function upgrade (line 20) | def upgrade() -> None:
  function downgrade (line 26) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0007_add_raw_file_models.py
  function upgrade (line 20) | def upgrade() -> None:
  function downgrade (line 37) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0008_point_normalizer_to_raw_file.py
  function upgrade (line 20) | def upgrade() -> None:
  function downgrade (line 29) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0009_max_length_on_varchar_s.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 69) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0010_longer_retrieval_link_string.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 31) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0011_longer_normalizer_name_and_boefje_id.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 38) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/0011_rename_normalizer_name_to_normalizer_id.py
  function upgrade (line 18) | def upgrade() -> None:
  function downgrade (line 24) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/09a2929108d9_add_signing_provider_model.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 34) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/1dde0213e9fe_remove_all_hash_mime_types.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 43) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/65a39ab3e224_increase_organization_and_input_ooi_size.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 38) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/d216ad75177d_add_environment_and_runnable_hash_.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 26) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/e09d8780e34b_nullable_input_ooi.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 25) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/e2f76e95f1e7_.py
  function upgrade (line 16) | def upgrade() -> None:
  function downgrade (line 20) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/ebc7de8be4e3_increase_organization_id_length.py
  function upgrade (line 19) | def upgrade() -> None:
  function downgrade (line 31) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/ec68d3eb14b1_remove_redundant_boefje_meta_foreign.py
  function upgrade (line 20) | def upgrade() -> None:
  function downgrade (line 38) | def downgrade() -> None:

FILE: bytes/bytes/database/migrations/versions/fa64454868a9_add_index_on_boefjemeta_table_.py
  function upgrade (line 18) | def upgrade() -> None:
  function downgrade (line 26) | def downgrade() -> None:

FILE: bytes/bytes/database/sql_meta_repository.py
  class SQLMetaDataRepository (line 23) | class SQLMetaDataRepository(MetaDataRepository):
    method __init__ (line 24) | def __init__(
    method __enter__ (line 32) | def __enter__(self) -> None:
    method __exit__ (line 35) | def __exit__(self, _exc_type: type[Exception], _exc_value: str, _exc_t...
    method save_boefje_meta (line 44) | def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:
    method get_boefje_meta_by_id (line 50) | def get_boefje_meta_by_id(self, boefje_meta_id: uuid.UUID) -> BoefjeMeta:
    method get_boefje_meta (line 58) | def get_boefje_meta(self, query_filter: BoefjeMetaFilter) -> list[Boef...
    method save_normalizer_meta (line 74) | def save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:
    method get_normalizer_meta_by_id (line 80) | def get_normalizer_meta_by_id(self, normalizer_meta_id: uuid.UUID) -> ...
    method get_normalizer_metas (line 88) | def get_normalizer_metas(
    method get_normalizer_meta (line 105) | def get_normalizer_meta(self, query_filter: NormalizerMetaFilter) -> l...
    method save_raw (line 131) | def save_raw(self, raw: RawData) -> uuid.UUID:
    method get_raw (line 153) | def get_raw(self, query_filter: RawDataFilter) -> list[RawDataMeta]:
    method get_raws (line 160) | def get_raws(self, query_filter: RawDataFilter) -> list[tuple[uuid.UUI...
    method get_raw_by_id (line 169) | def get_raw_by_id(self, raw_id: uuid.UUID) -> RawData:
    method get_raw_meta_by_id (line 178) | def get_raw_meta_by_id(self, raw_id: uuid.UUID) -> RawDataMeta:
    method has_raw (line 186) | def has_raw(self, boefje_meta: BoefjeMeta, mime_types: list[MimeType])...
    method get_raw_file_count_per_organization (line 196) | def get_raw_file_count_per_organization(self) -> dict[str, int]:
    method get_raw_file_count_per_mime_type (line 205) | def get_raw_file_count_per_mime_type(self, query_filter: RawDataFilter...
    method _to_raw (line 214) | def _to_raw(self, raw_file_in_db: RawFileInDB) -> RawData:
    method _get_or_create_signing_provider (line 220) | def _get_or_create_signing_provider(self, signing_provider_url: str | ...
  function create_meta_data_repository (line 234) | def create_meta_data_repository() -> Iterator[MetaDataRepository]:
  class ObjectNotFoundException (line 257) | class ObjectNotFoundException(Exception):
    method __init__ (line 258) | def __init__(self, cls: type[SQL_BASE], **kwargs: str):
  class MetaIntegrityError (line 262) | class MetaIntegrityError(Exception):
  function to_boefje_meta_in_db (line 266) | def to_boefje_meta_in_db(boefje_meta: BoefjeMeta) -> BoefjeMetaInDB:
  function to_boefje_meta (line 281) | def to_boefje_meta(boefje_meta_in_db: BoefjeMetaInDB) -> BoefjeMeta:
  function to_normalizer_meta_in_db (line 295) | def to_normalizer_meta_in_db(normalizer_meta: NormalizerMeta) -> Normali...
  function to_normalizer_meta (line 306) | def to_normalizer_meta(normalizer_meta_in_db: NormalizerMetaInDB) -> Nor...
  function to_raw_file_in_db (line 318) | def to_raw_file_in_db(raw_data: RawData, signing_provider: SigningProvid...
  function raw_meta_to_raw_file_in_db (line 329) | def raw_meta_to_raw_file_in_db(raw_data_meta: RawDataMeta, signing_provi...
  function to_raw_data (line 340) | def to_raw_data(raw_file_in_db: RawFileInDB, raw: bytes) -> RawData:
  function to_raw_meta (line 351) | def to_raw_meta(raw_file_in_db: RawFileInDB) -> RawDataMeta:
  function to_mime_type (line 362) | def to_mime_type(mime_type: str) -> MimeType:

FILE: bytes/bytes/events/events.py
  function utc_now (line 8) | def utc_now() -> datetime:
  class Event (line 12) | class Event(BaseModel):
  class RawFileReceived (line 19) | class RawFileReceived(Event):

FILE: bytes/bytes/events/manager.py
  class EventManager (line 4) | class EventManager:
    method publish (line 5) | async def publish(self, event: Event) -> None:

FILE: bytes/bytes/models.py
  class EncryptionMiddleware (line 15) | class EncryptionMiddleware(str, Enum):
  class HashingAlgorithm (line 20) | class HashingAlgorithm(str, Enum):
  class HashingRepositoryReference (line 25) | class HashingRepositoryReference(str, Enum):
  function _validate_timezone_aware_datetime (line 31) | def _validate_timezone_aware_datetime(value: datetime) -> datetime:
  class MimeType (line 38) | class MimeType(BaseModel):
    method __hash__ (line 41) | def __hash__(self) -> int:
    method __lt__ (line 44) | def __lt__(self, other: MimeType) -> bool:
  class Job (line 48) | class Job(BaseModel):
    method __hash__ (line 53) | def __hash__(self) -> int:
  class Boefje (line 57) | class Boefje(BaseModel):
  class Normalizer (line 62) | class Normalizer(BaseModel):
  class BoefjeMeta (line 67) | class BoefjeMeta(Job):
  class RawDataMeta (line 76) | class RawDataMeta(BaseModel):
  class RawData (line 89) | class RawData(BaseModel):
  class NormalizerMeta (line 100) | class NormalizerMeta(Job):

FILE: bytes/bytes/rabbitmq.py
  class RabbitMQEventManager (line 13) | class RabbitMQEventManager(EventManager):
    method __init__ (line 14) | def __init__(self, queue_uri: str):
    method _get_channel (line 19) | async def _get_channel(self) -> aio_pika.abc.AbstractChannel:
    method publish (line 30) | async def publish(self, event: Event) -> None:
    method _queue_name (line 42) | def _queue_name(event: Event) -> str:
  class NullManager (line 46) | class NullManager(EventManager):
    method publish (line 47) | async def publish(self, event: Event) -> None:
  function create_event_manager (line 51) | def create_event_manager() -> EventManager:

FILE: bytes/bytes/raw/file_raw_repository.py
  function create_raw_repository (line 24) | def create_raw_repository(settings: Settings) -> RawRepository:
  class FileRawRepository (line 41) | class FileRawRepository(RawRepository):
    method __init__ (line 44) | def __init__(
    method save_raw (line 57) | def save_raw(self, raw_id: UUID, raw: RawData) -> None:
    method get_raw (line 69) | def get_raw(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> RawData:
    method get_raws (line 78) | def get_raws(self, raw_metas_pairs: list[tuple[UUID, BoefjeMeta]]) -> ...
    method _raw_file_path (line 92) | def _raw_file_path(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> Path:
    method _index (line 95) | def _index(self, raw_id: UUID) -> str:
  class S3RawRepository (line 99) | class S3RawRepository(RawRepository):
    method __init__ (line 100) | def __init__(
    method get_or_create_bucket (line 111) | def get_or_create_bucket(self, organization: str) -> Bucket:
    method save_raw (line 123) | def save_raw(self, raw_id: UUID, raw: RawData) -> None:
    method get_raw (line 131) | def get_raw(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> RawData:
    method _raw_file_name (line 145) | def _raw_file_name(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> str:

FILE: bytes/bytes/raw/middleware.py
  class FileMiddleware (line 9) | class FileMiddleware:
    method encode (line 10) | def encode(self, contents: bytes) -> bytes:
    method decode (line 13) | def decode(self, contents: bytes) -> bytes:
  function make_middleware (line 17) | def make_middleware() -> FileMiddleware:
  class IdentityMiddleware (line 28) | class IdentityMiddleware(FileMiddleware):
    method encode (line 29) | def encode(self, contents: bytes) -> bytes:
    method decode (line 32) | def decode(self, contents: bytes) -> bytes:
  class NaclBoxMiddleware (line 36) | class NaclBoxMiddleware(FileMiddleware):
    method __init__ (line 37) | def __init__(self, kat_private: str, vws_public: str):
    method encode (line 42) | def encode(self, contents: bytes) -> bytes:
    method decode (line 45) | def decode(self, contents: bytes) -> bytes:

FILE: bytes/bytes/repositories/hash_repository.py
  class HashRepository (line 4) | class HashRepository:
    method store (line 7) | def store(self, secure_hash: SecureHash) -> RetrievalLink:
    method verify (line 12) | def verify(self, link: RetrievalLink, secure_hash: SecureHash) -> bool:
    method get_signing_provider_url (line 17) | def get_signing_provider_url(self) -> str | None:

FILE: bytes/bytes/repositories/meta_repository.py
  class BoefjeMetaFilter (line 10) | class BoefjeMetaFilter(BaseModel):
  class NormalizerMetaFilter (line 20) | class NormalizerMetaFilter(BaseModel):
  class RawDataFilter (line 29) | class RawDataFilter(BaseModel):
    method apply (line 38) | def apply(self, query: Query) -> Query:
  class MetaDataRepository (line 63) | class MetaDataRepository:
    method __enter__ (line 64) | def __enter__(self) -> None:
    method __exit__ (line 67) | def __exit__(self, _exc_type: type[Exception], _exc_value: str, _exc_t...
    method save_boefje_meta (line 70) | def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:
    method get_boefje_meta_by_id (line 73) | def get_boefje_meta_by_id(self, boefje_meta_id: UUID) -> BoefjeMeta:
    method get_boefje_meta (line 76) | def get_boefje_meta(self, query_filter: BoefjeMetaFilter) -> list[Boef...
    method save_normalizer_meta (line 79) | def save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:
    method get_normalizer_meta_by_id (line 82) | def get_normalizer_meta_by_id(self, normalizer_meta_id: UUID) -> Norma...
    method get_normalizer_meta (line 85) | def get_normalizer_meta(self, query_filter: NormalizerMetaFilter) -> l...
    method get_normalizer_metas (line 88) | def get_normalizer_metas(
    method save_raw (line 93) | def save_raw(self, raw: RawData) -> UUID:
    method get_raw_by_id (line 96) | def get_raw_by_id(self, raw_id: UUID) -> RawData:
    method get_raw (line 99) | def get_raw(self, query_filter: RawDataFilter) -> list[RawDataMeta]:
    method get_raws (line 102) | def get_raws(self, query_filter: RawDataFilter) -> list[tuple[UUID, Ra...
    method has_raw (line 105) | def has_raw(self, boefje_meta: BoefjeMeta, mime_types: list[MimeType])...
    method get_raw_file_count_per_organization (line 108) | def get_raw_file_count_per_organization(self) -> dict[str, int]:
    method get_raw_file_count_per_mime_type (line 111) | def get_raw_file_count_per_mime_type(self, query_filter: RawDataFilter...
    method get_raw_meta_by_id (line 114) | def get_raw_meta_by_id(self, raw_id: UUID) -> RawDataMeta:

FILE: bytes/bytes/repositories/raw_repository.py
  class BytesFileNotFoundException (line 6) | class BytesFileNotFoundException(FileNotFoundError):
  class RawRepository (line 10) | class RawRepository:
    method save_raw (line 11) | def save_raw(self, raw_id: UUID, raw: RawData) -> None:
    method get_raw (line 14) | def get_raw(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> RawData:
    method get_raws (line 17) | def get_raws(self, raw_metas_pairs: list[tuple[UUID, BoefjeMeta]]) -> ...

FILE: bytes/bytes/timestamping/hashing.py
  function hash_data (line 12) | def hash_data(
  function _get_hasher (line 25) | def _get_hasher(hash_algo: HashingAlgorithm) -> Any:

FILE: bytes/bytes/timestamping/in_memory.py
  class InMemoryHashRepository (line 7) | class InMemoryHashRepository(HashRepository):
    method __init__ (line 8) | def __init__(self, signing_provider_url: str | None = None) -> None:
    method store (line 12) | def store(self, secure_hash: SecureHash) -> RetrievalLink:
    method retrieve (line 17) | def retrieve(self, link: RetrievalLink) -> SecureHash:
    method verify (line 23) | def verify(self, link: RetrievalLink, secure_hash: SecureHash) -> bool:
    method get_signing_provider_url (line 26) | def get_signing_provider_url(self) -> str | None:

FILE: bytes/bytes/timestamping/pastebin.py
  class PastebinHashRepository (line 7) | class PastebinHashRepository(HashRepository):
    method __init__ (line 8) | def __init__(self, api_dev_key: str):
    method store (line 13) | def store(self, secure_hash: SecureHash) -> RetrievalLink:
    method retrieve (line 34) | def retrieve(self, link: RetrievalLink) -> SecureHash:
    method verify (line 50) | def verify(self, link: RetrievalLink, secure_hash: SecureHash) -> bool:
    method get_signing_provider_url (line 53) | def get_signing_provider_url(self) -> str | None:

FILE: bytes/bytes/timestamping/provider.py
  function create_hash_repository (line 9) | def create_hash_repository(settings: Settings) -> HashRepository:

FILE: bytes/bytes/timestamping/rfc3161.py
  function _patched_check_timestamp (line 15) | def _patched_check_timestamp(
  class RFC3161HashRepository (line 73) | class RFC3161HashRepository(HashRepository):
    method __init__ (line 76) | def __init__(self, certificate: bytes, signing_provider: str):
    method store (line 80) | def store(self, secure_hash: SecureHash) -> RetrievalLink:
    method verify (line 86) | def verify(self, link: RetrievalLink, secure_hash: SecureHash) -> bool:
    method get_signing_provider_url (line 98) | def get_signing_provider_url(self) -> str | None:

FILE: bytes/sql_migrations/0001_initial.sql
  type boefje_meta (line 8) | CREATE TABLE boefje_meta (
  type normalizer_meta (line 25) | CREATE TABLE normalizer_meta (
  type output_ooi (line 42) | CREATE TABLE output_ooi (

FILE: bytes/sql_migrations/0007_add_raw_file_models.sql
  type raw_file (line 1) | CREATE TABLE raw_file (

FILE: bytes/sql_migrations/0015_fa64454868a9_add_indices.sql
  type ix_boefje_meta_organization_boefje_id (line 1) | CREATE INDEX CONCURRENTLY ix_boefje_meta_organization_boefje_id ON boefj...
  type ix_normalizer_meta_raw_file_id (line 2) | CREATE INDEX CONCURRENTLY ix_normalizer_meta_raw_file_id ON normalizer_m...
  type ix_raw_file_boefje_meta_id (line 3) | CREATE INDEX CONCURRENTLY ix_raw_file_boefje_meta_id ON raw_file (boefje...

FILE: bytes/sql_migrations/0017_09a2929108d9_add_signing_provider_model.sql
  type signing_provider (line 1) | CREATE TABLE signing_provider (
  type ix_raw_file_signing_provider_id (line 10) | CREATE INDEX ix_raw_file_signing_provider_id ON raw_file (signing_provid...

FILE: bytes/tests/client.py
  function retry_with_login (line 20) | def retry_with_login(function: ClientSessionMethod) -> ClientSessionMethod:
  class BytesAPIClient (line 35) | class BytesAPIClient:
    method __init__ (line 36) | def __init__(self, base_url: str, username: str, password: str):
    method login (line 42) | def login(self) -> None:
    method _verify_response (line 46) | def _verify_response(response: httpx.Response) -> None:
    method _get_authentication_headers (line 49) | def _get_authentication_headers(self) -> dict[str, str]:
    method _get_token (line 52) | def _get_token(self) -> str:
    method get_metrics (line 60) | def get_metrics(self) -> bytes:
    method get_mime_type_count (line 68) | def get_mime_type_count(self, query_filter: RawDataFilter) -> dict[str...
    method save_boefje_meta (line 78) | def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:
    method get_boefje_meta_by_id (line 84) | def get_boefje_meta_by_id(self, boefje_meta_id: UUID) -> BoefjeMeta:
    method get_boefje_meta (line 92) | def get_boefje_meta(self, query_filter: BoefjeMetaFilter) -> list[Boef...
    method save_normalizer_meta (line 100) | def save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:
    method get_normalizer_meta_by_id (line 106) | def get_normalizer_meta_by_id(self, normalizer_meta_id: UUID) -> Norma...
    method get_normalizer_meta (line 114) | def get_normalizer_meta(self, query_filter: NormalizerMetaFilter) -> l...
    method save_raw (line 122) | def save_raw(self, boefje_meta_id: UUID, raw: bytes, mime_types: list[...
    method save_raws (line 137) | def save_raws(self, boefje_meta_id: UUID, boefje_output: BoefjeOutput)...
    method get_raw (line 146) | def get_raw(self, raw_id: UUID) -> bytes:
    method get_raw_meta (line 153) | def get_raw_meta(self, raw_id: UUID) -> RawDataMeta:
    method get_raw_metas (line 160) | def get_raw_metas(self, query_filter: RawDataFilter) -> dict[str, str]:
    method get_raws (line 170) | def get_raws(self, query_filter: RawDataFilter) -> list[File]:

FILE: bytes/tests/conftest.py
  function settings (line 25) | def settings(tmpdir):
  function test_client (line 44) | def test_client(settings: Settings) -> TestClient:
  function nacl_middleware (line 51) | def nacl_middleware(settings: Settings) -> NaclBoxMiddleware:
  function pastebin_hash_repository (line 56) | def pastebin_hash_repository(settings: Settings) -> HashRepository:
  function mock_hash_repository (line 61) | def mock_hash_repository(settings: Settings) -> HashRepository:
  function meta_repository (line 69) | def meta_repository(
  function bytes_api_client (line 88) | def bytes_api_client(settings) -> Iterator[BytesAPIClient]:
  function raw_repository (line 103) | def raw_repository(tmp_path: Path) -> FileRawRepository:
  function event_manager (line 108) | async def event_manager(settings: Settings) -> AsyncIterator[RabbitMQEve...

FILE: bytes/tests/integration/test_bytes_api.py
  function test_login (line 17) | def test_login(bytes_api_client: BytesAPIClient) -> None:
  function test_metrics (line 23) | def test_metrics(bytes_api_client: BytesAPIClient) -> None:
  function test_get_mime_type_count (line 74) | def test_get_mime_type_count(bytes_api_client: BytesAPIClient) -> None:
  function test_boefje_meta (line 93) | def test_boefje_meta(bytes_api_client: BytesAPIClient) -> None:
  function test_filtered_boefje_meta (line 109) | def test_filtered_boefje_meta(bytes_api_client: BytesAPIClient) -> None:
  function test_normalizer_meta (line 135) | async def test_normalizer_meta(bytes_api_client: BytesAPIClient, event_m...
  function test_filtered_normalizer_meta (line 156) | def test_filtered_normalizer_meta(bytes_api_client: BytesAPIClient) -> N...
  function test_normalizer_meta_pointing_to_raw_id (line 202) | def test_normalizer_meta_pointing_to_raw_id(bytes_api_client: BytesAPICl...
  function test_raw (line 221) | async def test_raw(bytes_api_client: BytesAPIClient, event_manager: Rabb...
  function test_raw_big (line 243) | async def test_raw_big(bytes_api_client: BytesAPIClient, event_manager: ...
  function test_save_raw_with_one_mime_type (line 264) | def test_save_raw_with_one_mime_type(bytes_api_client: BytesAPIClient) -...
  function test_save_raw_no_mime_types (line 284) | def test_save_raw_no_mime_types(bytes_api_client: BytesAPIClient) -> None:
  function test_get_many_actual_raw_files (line 310) | def test_get_many_actual_raw_files(bytes_api_client: BytesAPIClient) -> ...
  function test_raw_mimes (line 354) | def test_raw_mimes(bytes_api_client: BytesAPIClient) -> None:
  function test_cannot_overwrite_raw (line 413) | def test_cannot_overwrite_raw(bytes_api_client: BytesAPIClient) -> None:
  function test_save_multiple_raw_files (line 426) | def test_save_multiple_raw_files(bytes_api_client: BytesAPIClient) -> None:

FILE: bytes/tests/integration/test_event.py
  function test_event_published_successfully (line 12) | async def test_event_published_successfully(event_manager: RabbitMQEvent...

FILE: bytes/tests/integration/test_hash_service.py
  function test_save_raw_data_pastebin (line 11) | def test_save_raw_data_pastebin(

FILE: bytes/tests/integration/test_meta_repository.py
  function test_save_boefje_meta (line 13) | def test_save_boefje_meta(meta_repository: SQLMetaDataRepository) -> None:
  function test_data_error_is_raised_when_boefje_id_is_too_long (line 66) | def test_data_error_is_raised_when_boefje_id_is_too_long(meta_repository...
  function test_data_error_is_raised_when_organization_id_is_too_long (line 81) | def test_data_error_is_raised_when_organization_id_is_too_long(meta_repo...
  function test_save_raw (line 96) | def test_save_raw(meta_repository: SQLMetaDataRepository) -> None:
  function test_filter_raw_on_organization (line 151) | def test_filter_raw_on_organization(meta_repository: SQLMetaDataReposito...
  function test_filter_raw_not_on_organization (line 198) | def test_filter_raw_not_on_organization(meta_repository: SQLMetaDataRepo...
  function test_filter_normalizer_meta (line 226) | def test_filter_normalizer_meta(meta_repository: SQLMetaDataRepository) ...
  function test_save_normalizer_meta (line 284) | def test_save_normalizer_meta(meta_repository: SQLMetaDataRepository) ->...
  function test_normalizer_id_length (line 306) | def test_normalizer_id_length(meta_repository: SQLMetaDataRepository) ->...
  function test_normalizer_meta_pointing_to_raw_id (line 326) | def test_normalizer_meta_pointing_to_raw_id(meta_repository: SQLMetaData...

FILE: bytes/tests/integration/test_migrations.py
  function test_clean_mime_types (line 8) | def test_clean_mime_types(meta_repository: SQLMetaDataRepository) -> None:

FILE: bytes/tests/integration/test_timestamper.py
  function test_rfc3161_external_api (line 8) | def test_rfc3161_external_api(meta_repository: SQLMetaDataRepository, mo...

FILE: bytes/tests/loading.py
  function load_stub (line 11) | def load_stub(relative_path: str) -> dict[str, Any]:
  function load_stub_raw (line 17) | def load_stub_raw(relative_path: str) -> bytes:
  function get_boefje_meta (line 23) | def get_boefje_meta(
  function get_normalizer_meta (line 39) | def get_normalizer_meta(raw_file_id: UUID = UUID("2c9f47db-dfca-4928-b29...
  function get_raw_data (line 49) | def get_raw_data() -> RawData:
  function get_raw_data_meta (line 57) | def get_raw_data_meta(raw_file_id: UUID = UUID("2c9f47db-dfca-4928-b29f-...

FILE: bytes/tests/seed_database.py
  function seed (line 8) | def seed():

FILE: bytes/tests/unit/test_api.py
  function test_healthcheck (line 4) | def test_healthcheck(test_client) -> None:

FILE: bytes/tests/unit/test_auth.py
  function test_login_get_token (line 9) | def test_login_get_token(test_client: TestClient) -> None:
  function test_login_get_token_not_authorized (line 22) | def test_login_get_token_not_authorized(test_client: TestClient) -> None:

FILE: bytes/tests/unit/test_context_mapping.py
  function test_context_mapping_boefje (line 15) | def test_context_mapping_boefje() -> None:
  function test_context_mapping_normalizer (line 33) | def test_context_mapping_normalizer() -> None:
  function test_context_mapping_raw (line 54) | def test_context_mapping_raw() -> None:

FILE: bytes/tests/unit/test_hash.py
  class HashTests (line 10) | class HashTests(TestCase):
    method test_hash_same_data (line 11) | def test_hash_same_data(self) -> None:
    method test_hash_sha224 (line 30) | def test_hash_sha224(self) -> None:

FILE: bytes/tests/unit/test_raw_repository.py
  function has_encryption_keys (line 11) | def has_encryption_keys() -> bool:
  function test_save_raw (line 17) | def test_save_raw(raw_repository: FileRawRepository) -> None:
  function test_nacl_middleware (line 27) | def test_nacl_middleware(nacl_middleware: NaclBoxMiddleware) -> None:

FILE: cveapi/cveapi.py
  function download_files (line 15) | def download_files(directory: pathlib.Path, last_update: datetime | None...
  function run (line 65) | def run() -> None:

FILE: mula/scheduler/app.py
  class App (line 13) | class App:
    method __init__ (line 27) | def __init__(self, ctx: context.AppContext) -> None:
    method run (line 51) | def run(self) -> None:
    method start_schedulers (line 78) | def start_schedulers(self) -> None:
    method start_server (line 91) | def start_server(
    method start_collectors (line 104) | def start_collectors(self) -> None:
    method shutdown (line 109) | def shutdown(self) -> None:
    method _stop_threads (line 126) | def _stop_threads(self) -> None:
    method _unhandled_exception (line 140) | def _unhandled_exception(self, args: threading.ExceptHookArgs) -> None:
    method _collect_metrics (line 147) | def _collect_metrics(self) -> None:

FILE: mula/scheduler/clients/amqp/listeners.py
  class Listener (line 13) | class Listener(Connector):
    method __init__ (line 25) | def __init__(self) -> None:
    method listen (line 29) | def listen(self) -> None:
    method stop (line 32) | def stop(self) -> None:
    method log_future_exceptions (line 35) | def log_future_exceptions(self, fut: futures.Future):
  class RabbitMQ (line 41) | class RabbitMQ(Listener):
    method __init__ (line 78) | def __init__(self, dsn: str, queue: str, func: Callable, durable: bool...
    method listen (line 110) | def listen(self) -> None:
    method connect (line 114) | def connect(self, queue: str, durable: bool, prefetch_count: int) -> N...
    method basic_consume (line 149) | def basic_consume(self, queue: str, durable: bool, prefetch_count: int...
    method callback (line 172) | def callback(
    method dispatch (line 190) | def dispatch(self, channel: pika.channel.Channel, delivery_tag: int, b...
    method ack_message (line 207) | def ack_message(self, channel, delivery_tag):
    method stop (line 214) | def stop(self) -> None:
    method _close_callback (line 237) | def _close_callback(self):

FILE: mula/scheduler/clients/amqp/raw_data.py
  class RawData (line 4) | class RawData(RabbitMQ):

FILE: mula/scheduler/clients/amqp/scan_profile.py
  class ScanProfileMutation (line 4) | class ScanProfileMutation(RabbitMQ):

FILE: mula/scheduler/clients/connector.py
  class Connector (line 10) | class Connector:
    method __init__ (line 13) | def __init__(self):
    method is_host_available (line 16) | def is_host_available(self, hostname: str, port: int) -> bool:
    method is_host_healthy (line 32) | def is_host_healthy(self, host: str, health_endpoint: str) -> bool:
    method retry (line 51) | def retry(self, func: Callable, *args: Any, **kwargs: Any) -> bool:

FILE: mula/scheduler/clients/errors.py
  class ExternalServiceError (line 7) | class ExternalServiceError(Exception):
  class ExternalServiceConnectionError (line 11) | class ExternalServiceConnectionError(ExternalServiceError):
  class ExternalServiceResponseError (line 15) | class ExternalServiceResponseError(ExternalServiceError):
    method __init__ (line 16) | def __init__(self, message: str, response: httpx.Response):
  class ExternalServiceValidationError (line 21) | class ExternalServiceValidationError(ExternalServiceError):
  function exception_handler (line 25) | def exception_handler(func):

FILE: mula/scheduler/clients/http/client.py
  class HTTPClient (line 9) | class HTTPClient:
    method __init__ (line 12) | def __init__(self):
    method is_host_available (line 15) | def is_host_available(self, hostname: str, port: int) -> bool:
    method is_host_healthy (line 31) | def is_host_healthy(self, host: str, health_endpoint: str) -> bool:
    method retry (line 50) | def retry(self, func: Callable, *args, **kwargs) -> bool:

FILE: mula/scheduler/clients/http/external/bytes.py
  function retry_with_login (line 16) | def retry_with_login(function: ClientSessionMethod) -> ClientSessionMethod:
  class Bytes (line 33) | class Bytes(HTTPService):
    method __init__ (line 38) | def __init__(self, host: str, source: str, user: str, password: str, t...
    method login (line 54) | def login(self) -> None:
    method _verify_response (line 59) | def _verify_response(response: httpx.Response) -> None:
    method get_token (line 62) | def get_token(self) -> str:
    method get_last_run_boefje (line 72) | def get_last_run_boefje(self, boefje_id: str, input_ooi: str, organiza...
    method get_last_run_boefje_by_organisation_id (line 96) | def get_last_run_boefje_by_organisation_id(self, organization_id: str)...

FILE: mula/scheduler/clients/http/external/katalogus.py
  class Katalogus (line 10) | class Katalogus(HTTPService):
    method __init__ (line 15) | def __init__(self, host: str, source: str, timeout: int, pool_connecti...
    method get_boefjes (line 24) | def get_boefjes(self) -> list[Boefje]:
    method get_boefje (line 35) | def get_boefje(self, boefje_id: str) -> Boefje | None:
    method get_organisation (line 46) | def get_organisation(self, organisation_id) -> Organisation | None:
    method get_organisations (line 57) | def get_organisations(self) -> list[Organisation]:
    method get_plugins_by_organisation (line 68) | def get_plugins_by_organisation(self, organisation_id: str) -> list[Pl...
    method get_plugin_by_id_and_org_id (line 79) | def get_plugin_by_id_and_org_id(self, plugin_id: str, organisation_id:...
    method get_boefjes_by_type_and_org_id (line 91) | def get_boefjes_by_type_and_org_id(self, ooi_type: str, organisation_i...
    method get_normalizers_by_org_id_and_type (line 103) | def get_normalizers_by_org_id_and_type(self, organisation_id: str, ooi...
    method get_new_boefjes_by_org_id (line 113) | def get_new_boefjes_by_org_id(self, organisation_id: str) -> list[Plug...
    method get_configs (line 142) | def get_configs(

FILE: mula/scheduler/clients/http/external/octopoes.py
  class ListObjectsResponse (line 12) | class ListObjectsResponse(BaseModel):
  class Octopoes (line 17) | class Octopoes(HTTPService):
    method __init__ (line 23) | def __init__(self, host: str, source: str, orgs: list[Organisation], p...
    method get_objects_by_object_types (line 28) | def get_objects_by_object_types(
    method get_random_objects (line 65) | def get_random_objects(self, organisation_id: str, n: int, scan_level:...
    method get_objects (line 83) | def get_objects(self, organisation_id: str, references: list[str]) -> ...
    method get_object (line 92) | def get_object(self, organisation_id: str, reference: str) -> OOI | None:
    method get_object_clients (line 105) | def get_object_clients(self, reference: str, clients: set[str], valid_...
    method is_healthy (line 120) | def is_healthy(self) -> bool:

FILE: mula/scheduler/clients/http/external/rocky.py
  class Rocky (line 4) | class Rocky(HTTPService):

FILE: mula/scheduler/clients/http/service.py
  class HTTPService (line 13) | class HTTPService(Connector):
    method __init__ (line 43) | def __init__(self, host: str, source: str, timeout: int = 10, pool_con...
    method get (line 78) | def get(self, url: str, params: dict[str, Any] | None = None) -> httpx...
    method post (line 99) | def post(self, url: str, payload: dict[str, Any], params: dict[str, An...
    method _request_with_backoff (line 120) | def _request_with_backoff(self, method: str, url: str, **kwargs: Any) ...
    method headers (line 137) | def headers(self) -> MutableMapping[str, str]:
    method _do_checks (line 140) | def _do_checks(self) -> None:
    method is_healthy (line 165) | def is_healthy(self) -> bool:

FILE: mula/scheduler/config/settings.py
  class BackwardsCompatibleEnvSettings (line 16) | class BackwardsCompatibleEnvSettings(PydanticBaseSettingsSource):
    method get_field_value (line 19) | def get_field_value(self, field: fields.FieldInfo, field_name: str) ->...
    method __call__ (line 22) | def __call__(self) -> dict[str, Any]:
  class Settings (line 42) | class Settings(BaseSettings):
    method settings_customise_sources (line 169) | def settings_customise_sources(

FILE: mula/scheduler/context/context.py
  class AppContext (line 16) | class AppContext:
    method __init__ (line 40) | def __init__(self) -> None:

FILE: mula/scheduler/models/base.py
  class Base (line 4) | class Base(DeclarativeBase):

FILE: mula/scheduler/models/boefje.py
  class Boefje (line 8) | class Boefje(BaseModel):
  class BoefjeMeta (line 17) | class BoefjeMeta(BaseModel):
  class BoefjeConfig (line 30) | class BoefjeConfig(BaseModel):

FILE: mula/scheduler/models/errors.py
  class ValidationError (line 1) | class ValidationError(Exception):

FILE: mula/scheduler/models/events.py
  class RawDataReceivedEvent (line 8) | class RawDataReceivedEvent(BaseModel):

FILE: mula/scheduler/models/health.py
  class ServiceHealth (line 6) | class ServiceHealth(BaseModel):

FILE: mula/scheduler/models/normalizer.py
  class Normalizer (line 7) | class Normalizer(BaseModel):
  class NormalizerMeta (line 15) | class NormalizerMeta(BaseModel):

FILE: mula/scheduler/models/ooi.py
  class MutationOperationType (line 6) | class MutationOperationType(Enum):
  class RunOn (line 12) | class RunOn(Enum):
  class ScanProfile (line 17) | class ScanProfile(BaseModel):
  class OOI (line 23) | class OOI(BaseModel):
  class ScanProfileMutation (line 31) | class ScanProfileMutation(BaseModel):

FILE: mula/scheduler/models/organisation.py
  class Organisation (line 4) | class Organisation(BaseModel):

FILE: mula/scheduler/models/plugin.py
  class Plugin (line 8) | class Plugin(BaseModel):

FILE: mula/scheduler/models/queue.py
  class Queue (line 6) | class Queue(BaseModel):

FILE: mula/scheduler/models/raw_data.py
  class RawData (line 8) | class RawData(BaseModel):

FILE: mula/scheduler/models/schedule.py
  class Schedule (line 16) | class Schedule(BaseModel):
    method model_post_init (line 31) | def model_post_init(self, context) -> None:
    method validate_schedule (line 38) | def validate_schedule(cls, value: str) -> str:
  class ScheduleDB (line 50) | class ScheduleDB(Base):

FILE: mula/scheduler/models/scheduler.py
  class SchedulerType (line 8) | class SchedulerType(str, enum.Enum):
  class Scheduler (line 17) | class Scheduler(BaseModel):

FILE: mula/scheduler/models/task.py
  class TaskStatus (line 22) | class TaskStatus(str, enum.Enum):
  class Task (line 49) | class Task(BaseModel):
  class TaskDB (line 66) | class TaskDB(Base):
  class NormalizerTask (line 99) | class NormalizerTask(BaseModel):
    method hash (line 109) | def hash(self) -> str:
  class BoefjeTask (line 118) | class BoefjeTask(BaseModel):
    method hash (line 132) | def hash(self) -> str:
  class ReportTask (line 142) | class ReportTask(BaseModel):
    method hash (line 149) | def hash(self) -> str:

FILE: mula/scheduler/schedulers/errors.py
  function exception_handler (line 7) | def exception_handler(func):

FILE: mula/scheduler/schedulers/queue/errors.py
  class QueueEmptyError (line 4) | class QueueEmptyError(Exception):
  class NotAllowedError (line 8) | class NotAllowedError(Exception):
  class InvalidItemError (line 12) | class InvalidItemError(ValueError):
  class QueueFullError (line 16) | class QueueFullError(Full):
  class ItemNotFoundError (line 20) | class ItemNotFoundError(Exception):

FILE: mula/scheduler/schedulers/queue/pq.py
  function with_lock (line 18) | def with_lock(method):
  class PriorityQueue (line 27) | class PriorityQueue(abc.ABC):
    method __init__ (line 58) | def __init__(
    method pop (line 103) | def pop(self, limit: int | None = None, filters: storage.filters.Filte...
    method push (line 122) | def push(self, task: models.Task) -> models.Task:
    method peek (line 222) | def peek(self, index: int) -> models.Task | None:
    method remove (line 234) | def remove(self, task: models.Task) -> None:
    method clear (line 246) | def clear(self) -> None:
    method empty (line 251) | def empty(self) -> bool:
    method qsize (line 256) | def qsize(self) -> int:
    method full (line 261) | def full(self) -> bool:
    method is_item_on_queue (line 269) | def is_item_on_queue(self, task: models.Task) -> bool:
    method is_item_on_queue_by_hash (line 286) | def is_item_on_queue_by_hash(self, item_hash: str) -> bool:
    method get_item_by_identifier (line 299) | def get_item_by_identifier(self, task: models.Task) -> models.Task | N...
    method _is_valid_item (line 312) | def _is_valid_item(self, item: Any) -> bool:
    method dict (line 328) | def dict(self, include_pq: bool = True) -> dict[str, Any]:
    method create_hash (line 345) | def create_hash(self, task: models.Task) -> str:

FILE: mula/scheduler/schedulers/rankers/boefje.py
  class BoefjeRanker (line 7) | class BoefjeRanker(Ranker):
    method rank (line 15) | def rank(self, obj: Any) -> int:
  class BoefjeRankerTimeBased (line 53) | class BoefjeRankerTimeBased(Ranker):
    method rank (line 60) | def rank(self, obj: Any) -> int:

FILE: mula/scheduler/schedulers/rankers/normalizer.py
  class NormalizerRanker (line 6) | class NormalizerRanker(Ranker):
    method rank (line 11) | def rank(self, obj: Any) -> int:

FILE: mula/scheduler/schedulers/rankers/ranker.py
  class Ranker (line 9) | class Ranker(abc.ABC):
    method __init__ (line 26) | def __init__(self, ctx: context.AppContext) -> None:
    method rank (line 31) | def rank(self, obj: Any) -> int:

FILE: mula/scheduler/schedulers/scheduler.py
  class Scheduler (line 21) | class Scheduler(abc.ABC):
    method __init__ (line 59) | def __init__(
    method run (line 95) | def run(self) -> None:
    method log_future_exceptions (line 98) | def log_future_exceptions(self, fut: futures.Future):
    method run_in_thread (line 103) | def run_in_thread(
    method push_items_to_queue (line 122) | def push_items_to_queue(self, items: list[models.Task]) -> None:
    method push_item_to_queue_with_timeout (line 156) | def push_item_to_queue_with_timeout(
    method push_item_to_queue (line 186) | def push_item_to_queue(self, item: models.Task, create_schedule: bool ...
    method post_push (line 248) | def post_push(self, item: models.Task, create_schedule: bool = True) -...
    method pop_item_from_queue (line 329) | def pop_item_from_queue(
    method post_pop (line 358) | def post_pop(self, items: list[models.Task]) -> None:
    method calculate_deadline (line 365) | def calculate_deadline(self, schedule: models.Schedule) -> models.Sche...
    method calculate_default_deadline (line 381) | def calculate_default_deadline(self, schedule: models.Schedule) -> dat...
    method stop (line 398) | def stop(self) -> None:
    method stop_listeners (line 409) | def stop_listeners(self) -> None:
    method stop_threads (line 416) | def stop_threads(self) -> None:
    method is_space_on_queue (line 423) | def is_space_on_queue(self) -> bool:
    method is_item_on_queue_by_hash (line 436) | def is_item_on_queue_by_hash(self, item_hash: str) -> bool:
    method last_activity (line 440) | def last_activity(self) -> datetime | None:
    method last_activity (line 446) | def last_activity(self, value: datetime) -> None:
    method dict (line 451) | def dict(self) -> dict[str, Any]:

FILE: mula/scheduler/schedulers/schedulers/boefje.py
  class BoefjePQ (line 25) | class BoefjePQ(queue.PriorityQueue):
    method pop (line 33) | def pop(self, limit: int | None = None, filters: filters.FilterRequest...
  class BoefjeScheduler (line 43) | class BoefjeScheduler(Scheduler):
    method __init__ (line 54) | def __init__(self, ctx: context.AppContext):
    method run (line 71) | def run(self) -> None:
    method process_mutations (line 104) | def process_mutations(self, body: bytes) -> None:
    method process_new_boefjes (line 205) | def process_new_boefjes(self) -> None:
    method process_rescheduling (line 270) | def process_rescheduling(self):
    method push_boefje_task (line 440) | def push_boefje_task(self, boefje_task: models.BoefjeTask, create_sche...
    method push_item_to_queue (line 547) | def push_item_to_queue(self, item: models.Task, create_schedule: bool ...
    method has_boefje_permission_to_run (line 568) | def has_boefje_permission_to_run(self, boefje: models.Plugin, ooi: mod...
    method has_boefje_task_started_running (line 633) | def has_boefje_task_started_running(
    method has_boefje_task_stalled (line 682) | def has_boefje_task_stalled(self, task_db: models.Task | None, task: m...
    method has_boefje_task_grace_period_passed (line 704) | def has_boefje_task_grace_period_passed(
    method get_oois_for_boefje (line 752) | def get_oois_for_boefje(self, boefje: models.Plugin, organisation: str...
    method is_boefje_in_other_orgs (line 762) | def is_boefje_in_other_orgs(self, boefje_task: models.BoefjeTask) -> l...
    method calculate_deadline (line 861) | def calculate_deadline(self, schedule: models.Schedule) -> models.Sche...

FILE: mula/scheduler/schedulers/schedulers/normalizer.py
  class NormalizerScheduler (line 17) | class NormalizerScheduler(Scheduler):
    method __init__ (line 28) | def __init__(self, ctx: context.AppContext):
    method run (line 38) | def run(self) -> None:
    method process_raw_data (line 62) | def process_raw_data(self, body: bytes) -> None:
    method push_normalizer_task (line 166) | def push_normalizer_task(
    method push_item_to_queue (line 204) | def push_item_to_queue(self, item: models.Task, create_schedule: bool ...
    method has_normalizer_permission_to_run (line 225) | def has_normalizer_permission_to_run(self, normalizer: models.Plugin) ...
    method has_normalizer_task_started_running (line 242) | def has_normalizer_task_started_running(
    method has_raw_data_deschedule (line 265) | def has_raw_data_deschedule(self, raw_data: models.RawData) -> bool:
    method has_raw_data_errors (line 276) | def has_raw_data_errors(self, raw_data: models.RawData) -> bool:

FILE: mula/scheduler/schedulers/schedulers/report.py
  class ReportScheduler (line 15) | class ReportScheduler(Scheduler):
    method __init__ (line 22) | def __init__(self, ctx: context.AppContext):
    method run (line 31) | def run(self) -> None:
    method process_rescheduling (line 43) | def process_rescheduling(self):
    method push_report_task (line 72) | def push_report_task(

FILE: mula/scheduler/server/errors.py
  class FilterError (line 8) | class FilterError(storage.filters.errors.FilterError):
  class ValidationError (line 12) | class ValidationError(Exception):
  class StorageError (line 16) | class StorageError(storage.errors.StorageError):
  class NotFoundError (line 20) | class NotFoundError(Exception):
  class ConflictError (line 24) | class ConflictError(Exception):
  class BadRequestError (line 28) | class BadRequestError(Exception):
  class TooManyRequestsError (line 32) | class TooManyRequestsError(Exception):
  function filter_error_handler (line 36) | def filter_error_handler(request: fastapi.Request, exc: FilterError):
  function storage_error_handler (line 40) | def storage_error_handler(request: fastapi.Request, exc: StorageError):
  function validation_error_handler (line 46) | def validation_error_handler(request: fastapi.Request, exc: ValidationEr...
  function conflict_error_handler (line 52) | def conflict_error_handler(request: fastapi.Request, exc: ConflictError):
  function bad_request_error_handler (line 60) | def bad_request_error_handler(request: fastapi.Request, exc: BadRequestE...
  function not_found_error_handler (line 66) | def not_found_error_handler(request: fastapi.Request, exc: NotFoundError):
  function too_many_requests_error_handler (line 70) | def too_many_requests_error_handler(request: fastapi.Request, exc: TooMa...
  function http_error_handler (line 74) | def http_error_handler(request: fastapi.Request, exc: fastapi.HTTPExcept...

FILE: mula/scheduler/server/handlers/health.py
  class HealthAPI (line 9) | class HealthAPI:
    method __init__ (line 10) | def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext) -> N...
    method health (line 24) | def health(self, externals: bool = False) -> schemas.ServiceHealth:

FILE: mula/scheduler/server/handlers/metrics.py
  class MetricsAPI (line 19) | class MetricsAPI:
    method __init__ (line 20) | def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext) -> N...
    method metrics (line 51) | def metrics(self) -> Any:

FILE: mula/scheduler/server/handlers/root.py
  class RootAPI (line 9) | class RootAPI:
    method __init__ (line 12) | def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext):
    method root (line 20) | def root(self) -> Any:

FILE: mula/scheduler/server/handlers/schedulers.py
  class SchedulerAPI (line 11) | class SchedulerAPI:
    method __init__ (line 12) | def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext, s: d...
    method list (line 54) | def list(self) -> list[schemas.Scheduler]:
    method get (line 57) | def get(self, scheduler_id: str) -> schemas.Scheduler:
    method pop (line 64) | def pop(
    method push (line 79) | def push(self, scheduler_id: str, item: schemas.TaskPush) -> schemas.T...

FILE: mula/scheduler/server/handlers/schedules.py
  class ScheduleAPI (line 13) | class ScheduleAPI:
    method __init__ (line 14) | def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext, s: d...
    method list (line 74) | def list(
    method create (line 109) | def create(self, schedule: schemas.ScheduleCreate) -> schemas.Schedule:
    method get (line 139) | def get(self, schedule_id: uuid.UUID) -> schemas.Schedule:
    method patch (line 146) | def patch(self, schedule_id: uuid.UUID, schedule: schemas.SchedulePatc...
    method search (line 169) | def search(
    method delete (line 202) | def delete(self, schedule_id: uuid.UUID) -> None:

FILE: mula/scheduler/server/handlers/tasks.py
  class TaskAPI (line 13) | class TaskAPI:
    method __init__ (line 14) | def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext) -> N...
    method list (line 55) | def list(
    method get (line 85) | def get(self, task_id: uuid.UUID) -> schemas.Task:
    method patch (line 91) | def patch(self, task_id: uuid.UUID, item: schemas.TaskPatch) -> schema...
    method stats (line 107) | def stats(

FILE: mula/scheduler/server/schemas/health.py
  class ServiceHealth (line 6) | class ServiceHealth(BaseModel):

FILE: mula/scheduler/server/schemas/schedule.py
  class Schedule (line 7) | class Schedule(BaseModel):
  class ScheduleCreate (line 21) | class ScheduleCreate(BaseModel):
  class SchedulePatch (line 31) | class SchedulePatch(BaseModel):

FILE: mula/scheduler/server/schemas/scheduler.py
  class Scheduler (line 6) | class Scheduler(BaseModel):

FILE: mula/scheduler/server/schemas/task.py
  class TaskStatus (line 8) | class TaskStatus(str, enum.Enum):
  class Task (line 32) | class Task(BaseModel):
  class TaskPatch (line 46) | class TaskPatch(BaseModel):
  class TaskPush (line 60) | class TaskPush(BaseModel):
  class TaskPop (line 70) | class TaskPop(BaseModel):

FILE: mula/scheduler/server/server.py
  class Server (line 11) | class Server:
    method __init__ (line 22) | def __init__(self, ctx: context.AppContext, s: dict[str, schedulers.Sc...
    method run (line 54) | def run(self) -> None:

FILE: mula/scheduler/server/utils/pagination.py
  class PaginatedResponse (line 7) | class PaginatedResponse(BaseModel):
  function create_next_url (line 14) | def create_next_url(request: Request, offset: int, limit: int, count: in...
  function create_previous_url (line 21) | def create_previous_url(request: Request, offset: int, limit: int) -> st...
  function paginate (line 28) | def paginate(request: Request, items: list[Any], count: int, offset: int...

FILE: mula/scheduler/storage/connection.py
  class DBConn (line 11) | class DBConn:
    method __init__ (line 12) | def __init__(self, dsn: str, pool_size: int = 25):
    method connect (line 18) | def connect(self) -> None:

FILE: mula/scheduler/storage/errors.py
  class StorageError (line 6) | class StorageError(Exception):
  class IntegrityError (line 10) | class IntegrityError(Exception):
  function exception_handler (line 14) | def exception_handler(func):

FILE: mula/scheduler/storage/filters/casting.py
  function cast_expression (line 10) | def cast_expression(expression: BinaryExpression, filter_: Filter) -> Bi...

FILE: mula/scheduler/storage/filters/comparison.py
  class Comparator (line 6) | class Comparator:
    method __init__ (line 48) | def __init__(self, operator: str):
    method compare (line 60) | def compare(

FILE: mula/scheduler/storage/filters/errors.py
  class FilterError (line 1) | class FilterError(Exception):
  class UnsupportedTypeError (line 5) | class UnsupportedTypeError(FilterError):
  class MismatchedTypeError (line 9) | class MismatchedTypeError(FilterError):
  class ArgumentError (line 13) | class ArgumentError(FilterError):

FILE: mula/scheduler/storage/filters/filters.py
  class Filter (line 7) | class Filter(BaseModel):
  class FilterRequest (line 67) | class FilterRequest(BaseModel):
    method __init__ (line 77) | def __init__(self, *args, **kwargs):

FILE: mula/scheduler/storage/filters/functions.py
  function apply_filter (line 13) | def apply_filter(entity: DeclarativeBase, query: Query, filter_request: ...
  function is_relationship_property (line 114) | def is_relationship_property(attr) -> bool:

FILE: mula/scheduler/storage/migrations/env.py
  function run_migrations_offline (line 28) | def run_migrations_offline():
  function run_migrations_online (line 51) | def run_migrations_online():

FILE: mula/scheduler/storage/migrations/versions/0001_initial_migration.py
  function upgrade (line 19) | def upgrade():
  function downgrade (line 44) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0002_update_tasks.py
  function upgrade (line 19) | def upgrade():
  function downgrade (line 43) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0003_add_type_field_to_tasks.py
  function upgrade (line 19) | def upgrade():
  function downgrade (line 26) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0004_add_server_default.py
  function upgrade (line 20) | def upgrade():
  function downgrade (line 53) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0005_size_limit_hash.py
  function upgrade (line 19) | def upgrade():
  function downgrade (line 25) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0006_add_jsonb_fields_add_jsonb_fields.py
  function upgrade (line 20) | def upgrade():
  function downgrade (line 41) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0007_add_cancelled_status_for_tasks.py
  function upgrade (line 18) | def upgrade():
  function downgrade (line 24) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0008_add_task_schedule.py
  function upgrade (line 22) | def upgrade():
  function downgrade (line 87) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0009_add_organisation.py
  function upgrade (line 19) | def upgrade():
  function downgrade (line 44) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0010_add_indices.py
  function upgrade (line 19) | def upgrade():
  function downgrade (line 31) | def downgrade():

FILE: mula/scheduler/storage/migrations/versions/0011_add_more_indexes.py
  function upgrade (line 19) | def upgrade():
  function downgrade (line 39) | def downgrade():

FILE: mula/scheduler/storage/stores/pq.py
  class PriorityQueueStore (line 13) | class PriorityQueueStore:
    method __init__ (line 16) | def __init__(self, dbconn: DBConn) -> None:
    method build_pop_query (line 19) | def build_pop_query(self, session, scheduler_id: str | None = None, fi...
    method _pop_with_session (line 30) | def _pop_with_session(
    method pop (line 46) | def pop(
    method pop_boefje (line 52) | def pop_boefje(
    method push (line 94) | def push(self, item: models.Task) -> models.Task | None:
    method peek (line 103) | def peek(self, scheduler_id: str, index: int) -> models.Task | None:
    method update (line 122) | def update(self, scheduler_id: str, item: models.Task) -> None:
    method remove (line 134) | def remove(self, scheduler_id: str, item_id: UUID) -> None:
    method get (line 146) | def get(self, scheduler_id, item_id: UUID) -> models.Task | None:
    method empty (line 163) | def empty(self, scheduler_id: str) -> bool:
    method qsize (line 175) | def qsize(self, scheduler_id: str) -> int:
    method get_items (line 188) | def get_items(self, scheduler_id: str, filters: FilterRequest | None) ...
    method get_item_by_hash (line 206) | def get_item_by_hash(self, scheduler_id: str, item_hash: str) -> model...
    method get_items_by_scheduler_id (line 224) | def get_items_by_scheduler_id(self, scheduler_id: str) -> list[models....
    method get_active_task_by_schedule (line 237) | def get_active_task_by_schedule(self, schedule_id: str) -> models.Task...
    method clear (line 253) | def clear(self, scheduler_id: str) -> None:
    method bulk_update_status (line 264) | def bulk_update_status(self, scheduler_id: str, item_ids: list[UUID], ...

FILE: mula/scheduler/storage/stores/schedule.py
  class ScheduleStore (line 13) | class ScheduleStore:
    method __init__ (line 16) | def __init__(self, dbconn: DBConn) -> None:
    method get_schedules (line 21) | def get_schedules(
    method get_schedule (line 77) | def get_schedule(self, schedule_id: str) -> models.Schedule | None:
    method get_due_schedules (line 88) | def get_due_schedules(
    method get_schedule_by_hash (line 125) | def get_schedule_by_hash(self, schedule_hash: str) -> models.Schedule ...
    method create_schedule (line 138) | def create_schedule(self, schedule: models.Schedule) -> models.Schedule:
    method update_schedule (line 149) | def update_schedule(self, schedule: models.Schedule) -> None:
    method delete_schedule (line 159) | def delete_schedule(self, schedule_id: str) -> None:

FILE: mula/scheduler/storage/stores/task.py
  class TaskStore (line 12) | class TaskStore:
    method __init__ (line 15) | def __init__(self, dbconn: DBConn) -> None:
    method get_tasks (line 20) | def get_tasks(
    method get_task (line 68) | def get_task(self, task_id: str) -> models.Task | None:
    method get_tasks_by_hash (line 80) | def get_tasks_by_hash(self, task_hash: str, limit: int | None = None) ...
    method get_latest_task_by_hash (line 98) | def get_latest_task_by_hash(self, task_hash: str) -> models.Task | None:
    method create_task (line 116) | def create_task(self, task: models.Task) -> models.Task | None:
    method update_task (line 127) | def update_task(self, task: models.Task) -> None:
    method cancel_tasks (line 135) | def cancel_tasks(self, scheduler_id: str, task_ids: list[str]) -> None:
    method get_status_count_per_hour (line 143) | def get_status_count_per_hour(
    method get_status_counts (line 179) | def get_status_counts(

FILE: mula/scheduler/storage/utils.py
  function retry (line 12) | def retry(max_retries: int = 3, retry_delay: float = 5.0):

FILE: mula/scheduler/utils/cron.py
  function next_run (line 6) | def next_run(expression: str, start_time: datetime | None = None) -> dat...

FILE: mula/scheduler/utils/datastore.py
  class GUID (line 7) | class GUID(TypeDecorator):
    method load_dialect_impl (line 21) | def load_dialect_impl(self, dialect):
    method process_bind_param (line 27) | def process_bind_param(self, value, dialect):
    method process_result_value (line 39) | def process_result_value(self, value, dialect):

FILE: mula/scheduler/utils/dict_utils.py
  function deep_get (line 7) | def deep_get(d: Any | None, keys: list[str]) -> Any:
  class ExpiredError (line 13) | class ExpiredError(Exception):
  class ExpiringDict (line 17) | class ExpiringDict:
    method __init__ (line 23) | def __init__(self, lifetime: int = 300, start_time: datetime = datetim...
    method get (line 31) | def get(self, key: str, default: Any | None = None) -> Any:
    method is_empty (line 37) | def is_empty(self) -> bool:
    method reset (line 41) | def reset(self) -> None:
    method expiration_enabled (line 47) | def expiration_enabled(self) -> bool:
    method expiration_enabled (line 52) | def expiration_enabled(self, value: bool) -> None:
    method _is_expired (line 66) | def _is_expired(self) -> bool:
    method __getitem__ (line 69) | def __getitem__(self, key: str) -> Any:
    method __setitem__ (line 83) | def __setitem__(self, key: str, value: Any) -> None:
    method __delitem__ (line 87) | def __delitem__(self, key: str) -> None:
    method __contains__ (line 91) | def __contains__(self, key: str) -> bool:
    method __len__ (line 95) | def __len__(self) -> int:
    method __iter__ (line 99) | def __iter__(self) -> Iterator[str]:
    method setdefault (line 103) | def setdefault(self, key: str, default: Any) -> Any:

FILE: mula/scheduler/utils/errors.py
  class ValidationError (line 6) | class ValidationError(Exception):
  function validation_handler (line 10) | def validation_handler(func):

FILE: mula/scheduler/utils/functions.py
  function remove_trailing_slash (line 1) | def remove_trailing_slash(url: str) -> str:

FILE: mula/scheduler/utils/thread.py
  class ThreadRunner (line 8) | class ThreadRunner(threading.Thread):
    method __init__ (line 29) | def __init__(
    method run_forever (line 63) | def run_forever(self) -> None:
    method run_once (line 78) | def run_once(self) -> None:
    method run (line 91) | def run(self) -> None:
    method join (line 100) | def join(self, timeout: float | None = None) -> None:
    method stop (line 108) | def stop(self) -> None:

FILE: mula/scripts/benchmark.py
  function are_tasks_done (line 19) | def are_tasks_done() -> bool:
  function parse_stats (line 33) | def parse_stats() -> None:
  function capture_logs (line 52) | def capture_logs(container_id: str, output_file: str) -> None:
  function parse_logs (line 58) | def parse_logs(path: str) -> None:
  function collect_cpu (line 71) | def collect_cpu(container_id: str) -> str:
  function collect_memory (line 83) | def collect_memory(container_id: str) -> str:
  function run (line 96) | def run(container_id: str) -> None:

FILE: mula/scripts/load.py
  function create_organisations (line 23) | def create_organisations(org_num: int = 1) -> list[dict[str, Any]]:
  function create_oois (line 58) | def create_oois(orgs: list[dict[str, Any]], ooi_num: int = 10) -> None:
  function enable_boefjes (line 125) | def enable_boefjes(orgs: list[dict[str, Any]], boefjes_str: str = "dns-r...
  function run (line 147) | def run(org_num: int = 1, ooi_num: int = 10, boefjes_str: str = "dns-rec...

FILE: mula/tests/factories/boefje.py
  class BoefjeFactory (line 8) | class BoefjeFactory(Factory):
    class Meta (line 9) | class Meta:
  class BoefjeMetaFactory (line 17) | class BoefjeMetaFactory(Factory):
    class Meta (line 18) | class Meta:

FILE: mula/tests/factories/normalizer.py
  class NormalizerFactory (line 5) | class NormalizerFactory(Factory):
    class Meta (line 6) | class Meta:

FILE: mula/tests/factories/ooi.py
  class ScanProfileFactory (line 5) | class ScanProfileFactory(Factory):
    class Meta (line 6) | class Meta:
  class OOIFactory (line 16) | class OOIFactory(Factory):
    class Meta (line 17) | class Meta:

FILE: mula/tests/factories/organisation.py
  class OrganisationFactory (line 5) | class OrganisationFactory(Factory):
    class Meta (line 6) | class Meta:

FILE: mula/tests/factories/plugin.py
  class PluginFactory (line 6) | class PluginFactory(Factory):
    class Meta (line 7) | class Meta:

FILE: mula/tests/factories/raw_data.py
  class RawDataFactory (line 5) | class RawDataFactory(Factory):
    class Meta (line 6) | class Meta:

FILE: mula/tests/integration/test_api.py
  class APITemplateTestCase (line 22) | class APITemplateTestCase(unittest.TestCase):
    method setUp (line 23) | def setUp(self):
    method tearDown (line 62) | def tearDown(self):
  class APISchedulerEndpointTestCase (line 68) | class APISchedulerEndpointTestCase(APITemplateTestCase):
    method test_get_schedulers (line 69) | def test_get_schedulers(self):
    method test_get_scheduler (line 73) | def test_get_scheduler(self):
    method test_get_scheduler_malformed_id (line 78) | def test_get_scheduler_malformed_id(self):
    method test_push_queue (line 82) | def test_push_queue(self):
    method test_push_queue_malformed_item (line 103) | def test_push_queue_malformed_item(self):
    method test_push_incorrect_item_type (line 111) | def test_push_incorrect_item_type(self):
    method test_push_queue_full (line 118) | def test_push_queue_full(self):
    method test_push_queue_full_high_priority (line 134) | def test_push_queue_full_high_priority(self):
    method test_push_replace_not_allowed (line 150) | def test_push_replace_not_allowed(self):
    method test_push_replace_allowed (line 173) | def test_push_replace_allowed(self):
    method test_push_updates_not_allowed (line 196) | def test_push_updates_not_allowed(self):
    method test_push_updates_allowed (line 223) | def test_push_updates_allowed(self):
    method test_push_priority_updates_not_allowed (line 251) | def test_push_priority_updates_not_allowed(self):
    method test_update_priority_higher (line 277) | def test_update_priority_higher(self):
    method test_update_priority_lower (line 307) | def test_update_priority_lower(self):
    method test_pop_queue (line 339) | def test_pop_queue(self):
    method test_pop_queue_multiple (line 357) | def test_pop_queue_multiple(self):
    method test_pop_queue_multiple_pagination (line 386) | def test_pop_queue_multiple_pagination(self):
    method test_pop_queue_not_found (line 418) | def test_pop_queue_not_found(self):
    method test_pop_queue_filters_two_items (line 423) | def test_pop_queue_filters_two_items(self):
    method test_pop_queue_filters_one_item (line 455) | def test_pop_queue_filters_one_item(self):
    method test_pop_queue_filters_nested (line 490) | def test_pop_queue_filters_nested(self):
    method test_pop_queue_filters_nested_contained_by (line 542) | def test_pop_queue_filters_nested_contained_by(self):
    method test_pop_empty (line 574) | def test_pop_empty(self):
  class APITasksEndpointTestCase (line 581) | class APITasksEndpointTestCase(APITemplateTestCase):
    method setUp (line 582) | def setUp(self):
    method test_create_task (line 607) | def test_create_task(self):
    method test_get_tasks (line 626) | def test_get_tasks(self):
    method test_get_task (line 632) | def test_get_task(self):
    method test_get_task_malformed_id (line 645) | def test_get_task_malformed_id(self):
    method test_get_task_not_found (line 650) | def test_get_task_not_found(self):
    method test_get_tasks_min_and_max_created_at (line 656) | def test_get_tasks_min_and_max_created_at(self):
    method test_get_tasks_min_created_at (line 676) | def test_get_tasks_min_created_at(self):
    method test_get_tasks_max_created_at (line 690) | def test_get_tasks_max_created_at(self):
    method test_get_tasks_min_greater_than_max_created_at (line 704) | def test_get_tasks_min_greater_than_max_created_at(self):
    method test_get_tasks_min_created_at_future (line 716) | def test_get_tasks_min_created_at_future(self):
    method test_get_normalizer_filtered (line 723) | def test_get_normalizer_filtered(self):
    method test_get_tasks_filtered (line 730) | def test_get_tasks_filtered(self):
    method test_get_tasks_by_schedule (line 750) | def test_get_tasks_by_schedule(self):
    method test_patch_task (line 784) | def test_patch_task(self):
    method test_patch_task_empty (line 791) | def test_patch_task_empty(self):
    method test_patch_task_invalid_content (line 797) | def test_patch_task_invalid_content(self):
    method test_patch_task_not_found (line 803) | def test_patch_task_not_found(self):
    method test_patch_task_malformed_id (line 810) | def test_patch_task_malformed_id(self):
    method test_patch_task_invalid_status (line 816) | def test_patch_task_invalid_status(self):
    method test_get_tasks_stats (line 822) | def test_get_tasks_stats(self):
  class APIScheduleEndpointTestCase (line 833) | class APIScheduleEndpointTestCase(APITemplateTestCase):
    method setUp (line 834) | def setUp(self):
    method test_list_schedules (line 859) | def test_list_schedules(self):
    method test_list_schedules_scheduler_id (line 865) | def test_list_schedules_scheduler_id(self):
    method test_list_schedules_enabled (line 876) | def test_list_schedules_enabled(self):
    method test_list_schedules_min_deadline (line 887) | def test_list_schedules_min_deadline(self):
    method test_list_schedules_max_deadline (line 899) | def test_list_schedules_max_deadline(self):
    method test_list_schedules_min_and_max_deadline (line 911) | def test_list_schedules_min_and_max_deadline(self):
    method test_list_schedules_min_greater_than_max_deadline (line 928) | def test_list_schedules_min_greater_than_max_deadline(self):
    method test_list_schedules_hash (line 937) | def test_list_schedules_hash(self):
    method test_list_schedules_min_created_at (line 944) | def test_list_schedules_min_created_at(self):
    method test_list_schedules_max_created_at (line 956) | def test_list_schedules_max_created_at(self):
    method test_list_schedules_min_and_max_created_at (line 968) | def test_list_schedules_min_and_max_created_at(self):
    method test_list_schedules_organisation (line 984) | def test_list_schedules_organisation(self):
    method test_post_schedule (line 995) | def test_post_schedule(self):
    method test_post_schedule_explicit_deadline_at (line 1018) | def test_post_schedule_explicit_deadline_at(self):
    method test_post_schedule_schedule_and_deadline_at_none (line 1040) | def test_post_schedule_schedule_and_deadline_at_none(self):
    method test_post_schedule_invalid_schedule (line 1052) | def test_post_schedule_invalid_schedule(self):
    method test_post_schedule_invalid_scheduler_id (line 1066) | def test_post_schedule_invalid_scheduler_id(self):
    method test_post_schedule_invalid_data (line 1080) | def test_post_schedule_invalid_data(self):
    method test_post_schedule_invalid_data_type (line 1093) | def test_post_schedule_invalid_data_type(self):
    method test_post_schedule_hash_already_exists (line 1107) | def test_post_schedule_hash_already_exists(self):
    method test_get_schedule (line 1132) | def test_get_schedule(self):
    method test_patch_schedule (line 1137) | def test_patch_schedule(self):
    method test_patch_schedule_validate_schedule (line 1142) | def test_patch_schedule_validate_schedule(self):
    method test_patch_schedule_validate_malformed_schedule (line 1147) | def test_patch_schedule_validate_malformed_schedule(self):
    method test_search_schedule (line 1152) | def test_search_schedule(self):
    method test_search_schedule_with_pagination (line 1165) | def test_search_schedule_with_pagination(self):
    method test_delete_schedule (line 1174) | def test_delete_schedule(self):
    method test_delete_schedule_task_schedule_id (line 1182) | def test_delete_schedule_task_schedule_id(self):

FILE: mula/tests/integration/test_app.py
  class AppTestCase (line 14) | class AppTestCase(unittest.TestCase):
    method setUp (line 15) | def setUp(self):
    method tearDown (line 38) | def tearDown(self):
    method test_shutdown (line 43) | def test_shutdown(self):

FILE: mula/tests/integration/test_boefje_scheduler.py
  class BoefjeSchedulerBaseTestCase (line 25) | class BoefjeSchedulerBaseTestCase(unittest.TestCase):
    method setUp (line 26) | def setUp(self):
    method tearDown (line 67) | def tearDown(self):
  class BoefjeSchedulerTestCase (line 73) | class BoefjeSchedulerTestCase(BoefjeSchedulerBaseTestCase):
    method setUp (line 74) | def setUp(self):
    method tearDown (line 97) | def tearDown(self):
    method test_run (line 100) | def test_run(self):
    method test_is_allowed_to_run (line 115) | def test_is_allowed_to_run(self):
    method test_is_allowed_to_run_no_ooi (line 127) | def test_is_allowed_to_run_no_ooi(self):
    method test_is_not_allowed_to_run (line 139) | def test_is_not_allowed_to_run(self):
    method test_is_task_not_running (line 153) | def test_is_task_not_running(self):
    method test_has_boefje_task_started_running_datastore_running (line 173) | def test_has_boefje_task_started_running_datastore_running(self):
    method test_has_boefje_task_started_running_datastore_not_running (line 197) | def test_has_boefje_task_started_running_datastore_not_running(self):
    method test_has_boefje_task_started_running_datastore_exception (line 245) | def test_has_boefje_task_started_running_datastore_exception(self):
    method test_has_boefje_task_started_running_bytes_running (line 260) | def test_has_boefje_task_started_running_bytes_running(self):
    method test_has_boefje_task_started_running_bytes_not_running (line 280) | def test_has_boefje_task_started_running_bytes_not_running(self):
    method test_has_boefje_task_started_running_stalled_before_grace_period (line 300) | def test_has_boefje_task_started_running_stalled_before_grace_period(s...
    method test_has_boefje_task_started_running_stalled_after_grace_period (line 328) | def test_has_boefje_task_started_running_stalled_after_grace_period(se...
    method test_has_boefje_task_started_running_mismatch_before_grace_period (line 356) | def test_has_boefje_task_started_running_mismatch_before_grace_period(...
    method test_has_boefje_task_started_running_mismatch_after_grace_period (line 388) | def test_has_boefje_task_started_running_mismatch_after_grace_period(s...
    method test_has_boefje_task_grace_period_passed_datastore_passed (line 421) | def test_has_boefje_task_grace_period_passed_datastore_passed(self):
    method test_has_boefje_task_grace_period_passed_datastore_not_passed (line 453) | def test_has_boefje_task_grace_period_passed_datastore_not_passed(self):
    method test_has_boefje_task_grace_period_passed_bytes_passed (line 485) | def test_has_boefje_task_grace_period_passed_bytes_passed(self):
    method test_has_boefje_task_grace_period_passed_bytes_not_passed (line 521) | def test_has_boefje_task_grace_period_passed_bytes_not_passed(self):
    method test_push_boefje_task (line 553) | def test_push_boefje_task(self):
    method test_push_boefje_task_no_ooi (line 577) | def test_push_boefje_task_no_ooi(self):
    method test_push_boefje_task_queue_full (line 602) | def test_push_boefje_task_queue_full(
    method test_push_boefje_task_stalled (line 651) | def test_push_boefje_task_stalled(
    method test_push_boefje_task_boefje_in_other_orgs (line 728) | def test_push_boefje_task_boefje_in_other_orgs(self):
    method test_push_boefje_task_boefje_in_other_orgs_one_org (line 812) | def test_push_boefje_task_boefje_in_other_orgs_one_org(self):
    method test_push_boefje_task_boefje_in_other_orgs_no_configs (line 860) | def test_push_boefje_task_boefje_in_other_orgs_no_configs(self):
    method test_push_boefje_task_boefje_in_other_orgs_no_ooi (line 907) | def test_push_boefje_task_boefje_in_other_orgs_no_ooi(self):
    method test_post_push (line 965) | def test_post_push(self):
    method test_post_push_boefje_cron (line 1013) | def test_post_push_boefje_cron(self):
    method test_post_push_boefje_interval (line 1070) | def test_post_push_boefje_interval(self):
    method test_pop (line 1121) | def test_pop(self):
    method test_pop_deduplication (line 1154) | def test_pop_deduplication(self):
    method test_pop_deduplication_different_deduplication_key (line 1195) | def test_pop_deduplication_different_deduplication_key(self):
    method test_post_pop (line 1258) | def test_post_pop(self):
    method test_has_boefje_permission_to_run (line 1304) | def test_has_boefje_permission_to_run(self):
    method test_has_boefje_permission_to_run_boefje_disabled (line 1316) | def test_has_boefje_permission_to_run_boefje_disabled(self):
    method test_has_boefje_permission_to_run_scan_profile_is_none (line 1328) | def test_has_boefje_permission_to_run_scan_profile_is_none(self):
    method test_has_boefje_permission_to_run_ooi_scan_level_is_none (line 1341) | def test_has_boefje_permission_to_run_ooi_scan_level_is_none(self):
    method test_has_boefje_permission_to_run_boefje_scan_level_is_none (line 1354) | def test_has_boefje_permission_to_run_boefje_scan_level_is_none(self):
  class ScanProfileMutationTestCase (line 1367) | class ScanProfileMutationTestCase(BoefjeSchedulerBaseTestCase):
    method setUp (line 1368) | def setUp(self):
    method tearDown (line 1395) | def tearDown(self):
    method test_process_mutations__ (line 1398) | def test_process_mutations__(self):
    method test_process_mutations_value_empty (line 1425) | def test_process_mutations_value_empty(self):
    method test_process_mutations_no_boefjes_found (line 1438) | def test_process_mutations_no_boefjes_found(self):
    method test_process_mutations_not_allowed_to_run (line 1456) | def test_process_mutations_not_allowed_to_run(self):
    method test_process_mutations_still_running (line 1476) | def test_process_mutations_still_running(self):
    method test_process_mutations_item_on_queue (line 1496) | def test_process_mutations_item_on_queue(self):
    method test_process_mutations_delete (line 1528) | def test_process_mutations_delete(self):
    method test_process_mutations_delete_on_queue (line 1551) | def test_process_mutations_delete_on_queue(self):
    method test_process_mutations_op_create_run_on_create (line 1600) | def test_process_mutations_op_create_run_on_create(self):
    method test_process_mutations_op_create_run_on_create_update (line 1640) | def test_process_mutations_op_create_run_on_create_update(self):
    method test_process_mutations_op_create_run_on_update (line 1680) | def test_process_mutations_op_create_run_on_update(self):
    method test_process_mutations_op_create_run_on_none (line 1707) | def test_process_mutations_op_create_run_on_none(self):
    method test_process_mutations_op_update_run_on_create (line 1750) | def test_process_mutations_op_update_run_on_create(self):
    method test_process_mutations_op_update_run_on_create_update (line 1778) | def test_process_mutations_op_update_run_on_create_update(self):
    method test_process_mutations_op_update_run_on_update (line 1819) | def test_process_mutations_op_update_run_on_update(self):
    method test_process_mutations_op_update_run_on_none (line 1860) | def test_process_mutations_op_update_run_on_none(self):
  class NewBoefjesTestCase (line 1902) | class NewBoefjesTestCase(BoefjeSchedulerBaseTestCase):
    method setUp (line 1903) | def setUp(self):
    method tearDown (line 1934) | def tearDown(self):
    method test_process_new_boefjes (line 1937) | def test_process_new_boefjes(self):
    method test_process_new_boefjes_request_exception (line 1963) | def test_process_new_boefjes_request_exception(self):
    method test_process_new_boefjes_no_new_boefjes (line 1983) | def test_process_new_boefjes_no_new_boefjes(self):
    method test_process_new_boefjes_empty_consumes (line 1998) | def test_process_new_boefjes_empty_consumes(self):
    method test_process_new_boefjes_empty_consumes_no_ooi (line 2014) | def test_process_new_boefjes_empty_consumes_no_ooi(self):
    method test_process_new_boefjes_no_oois_found (line 2028) | def test_process_new_boefjes_no_oois_found(self):
    method test_process_new_boefjes_get_objects_request_exception (line 2044) | def test_process_new_boefjes_get_objects_request_exception(self):
    method test_process_new_boefjes_not_allowed_to_run (line 2064) | def test_process_new_boefjes_not_allowed_to_run(self):
    method test_process_new_boefjes_still_running (line 2081) | def test_process_new_boefjes_still_running(self):
    method test_process_new_boefjes_item_on_queue (line 2098) | def test_process_new_boefjes_item_on_queue(self):
  class RescheduleTestCase (line 2132) | class RescheduleTestCase(BoefjeSchedulerBaseTestCase):
    method setUp (line 2133) | def setUp(self):
    method tearDown (line 2160) | def tearDown(self):
    method test_process_rescheduling_scheduler_id (line 2163) | def test_process_rescheduling_scheduler_id(self):
    method test_process_rescheduling (line 2166) | def test_process_rescheduling(self):
    method test_process_rescheduling_no_ooi (line 2209) | def test_process_rescheduling_no_ooi(self):
    method test_process_rescheduling_ooi_not_found (line 2255) | def test_process_rescheduling_ooi_not_found(self):
    method test_process_rescheduling_boefje_not_found (line 2294) | def test_process_rescheduling_boefje_not_found(self):
    method test_process_rescheduling_boefje_disabled (line 2333) | def test_process_rescheduling_boefje_disabled(self):
    method test_process_rescheduling_boefje_doesnt_consume_ooi (line 2372) | def test_process_rescheduling_boefje_doesnt_consume_ooi(self):
    method test_process_rescheduling_boefje_cannot_scan_ooi (line 2411) | def test_process_rescheduling_boefje_cannot_scan_ooi(self):

FILE: mula/tests/integration/test_clients.py
  class BytesTestCase (line 12) | class BytesTestCase(unittest.TestCase):
    method setUp (line 13) | def setUp(self) -> None:
    method test_login (line 25) | def test_login(self):
    method test_expired_token_refresh (line 32) | def test_expired_token_refresh(self):
  class KatalogusTestCase (line 44) | class KatalogusTestCase(unittest.TestCase):
    method setUp (line 45) | def setUp(self) -> None:
    method test_get_new_boefjes_by_org_id (line 58) | def test_get_new_boefjes_by_org_id(self, mock_get_plugins_by_organisat...
    method test_new_boefjes_cache_thread_safety (line 113) | def test_new_boefjes_cache_thread_safety(self, mock_get_plugins_by_org...

FILE: mula/tests/integration/test_listeners.py
  class RabbitMQTestCase (line 12) | class RabbitMQTestCase(unittest.TestCase):
    method setUp (line 15) | def setUp(self):
    method tearDown (line 20) | def tearDown(self):
    method unhandled_exception (line 24) | def unhandled_exception(self, args: threading.ExceptHookArgs) -> None:
    method test_shutdown (line 29) | def test_shutdown(self):
    method test_shutdown_no_connection (line 60) | def test_shutdown_no_connection(self):
    method test_start_consuming_exception (line 94) | def test_start_consuming_exception(self, mock_start_consuming):
    method test_func_exception (line 127) | def test_func_exception(self):

FILE: mula/tests/integration/test_normalizer_scheduler.py
  class NormalizerSchedulerBaseTestCase (line 23) | class NormalizerSchedulerBaseTestCase(unittest.TestCase):
    method setUp (line 24) | def setUp(self):
    method tearDown (line 49) | def tearDown(self):
  class NormalizerSchedulerTestCase (line 55) | class NormalizerSchedulerTestCase(NormalizerSchedulerBaseTestCase):
    method setUp (line 56) | def setUp(self):
    method test_is_allowed_to_run (line 67) | def test_is_allowed_to_run(self):
    method test_is_not_allowed_to_run (line 80) | def test_is_not_allowed_to_run(self):
  class RawFileReceivedTestCase (line 95) | class RawFileReceivedTestCase(NormalizerSchedulerBaseTestCase):
    method setUp (line 96) | def setUp(self):
    method test_process_raw_data (line 115) | def test_process_raw_data(self):
    method test_process_raw_data_no_normalizers_found (line 144) | def test_process_raw_data_no_normalizers_found(self):
    method test_process_raw_data_not_allowed_to_run (line 165) | def test_process_raw_data_not_allowed_to_run(self):
    method test_process_raw_data_still_running (line 195) | def test_process_raw_data_still_running(self):
    method test_process_raw_data_still_running_exception (line 226) | def test_process_raw_data_still_running_exception(self):
    method test_process_raw_data_item_on_queue (line 257) | def test_process_raw_data_item_on_queue(self):
    method test_process_raw_data_error_mimetype (line 291) | def test_process_raw_data_error_mimetype(self):
    method test_process_raw_data_queue_full (line 317) | def test_process_raw_data_queue_full(self):

FILE: mula/tests/integration/test_pq_store.py
  class PriorityQueueStoreTestCase (line 13) | class PriorityQueueStoreTestCase(unittest.TestCase):
    method setUp (line 14) | def setUp(self):
    method tearDown (line 35) | def tearDown(self):
    method test_push (line 39) | def test_push(self):
    method test_push_status_not_queued (line 52) | def test_push_status_not_queued(self):
    method test_pop (line 63) | def test_pop(self):
    method test_pop_status_not_queued (line 76) | def test_pop_status_not_queued(self):

FILE: mula/tests/integration/test_report_scheduler.py
  class ReportSchedulerBaseTestCase (line 11) | class ReportSchedulerBaseTestCase(unittest.TestCase):
    method setUp (line 12) | def setUp(self):
    method tearDown (line 37) | def tearDown(self):
  class ReportSchedulerTestCase (line 43) | class ReportSchedulerTestCase(ReportSchedulerBaseTestCase):
    method setUp (line 44) | def setUp(self):
    method tearDown (line 51) | def tearDown(self):
    method test_process_rescheduling (line 54) | def test_process_rescheduling(self):
    method test_process_rescheduling_item_on_queue (line 86) | def test_process_rescheduling_item_on_queue(self):

FILE: mula/tests/integration/test_schedule_store.py
  class ScheduleStoreTestCase (line 13) | class ScheduleStoreTestCase(unittest.TestCase):
    method setUp (line 14) | def setUp(self):
    method tearDown (line 35) | def tearDown(self):
    method test_create_schedule_calculate_deadline_at (line 39) | def test_create_schedule_calculate_deadline_at(self):
    method test_create_schedule_explicit_deadline_at (line 47) | def test_create_schedule_explicit_deadline_at(self):
    method test_create_schedule_deadline_at_takes_precedence (line 56) | def test_create_schedule_deadline_at_takes_precedence(self):
    method test_create_schedule (line 69) | def test_create_schedule(self):
    method test_get_schedules (line 84) | def test_get_schedules(self):
    method test_get_schedule (line 119) | def test_get_schedule(self):
    method test_get_schedule_by_hash (line 134) | def test_get_schedule_by_hash(self):
    method test_update_schedule (line 151) | def test_update_schedule(self):
    method test_delete_schedule (line 171) | def test_delete_schedule(self):
    method test_delete_schedule_ondelete (line 187) | def test_delete_schedule_ondelete(self):
    method test_relationship_schedule_tasks (line 220) | def test_relationship_schedule_tasks(self):
    method test_get_tasks_filter_related (line 239) | def test_get_tasks_filter_related(self):

FILE: mula/tests/integration/test_scheduler.py
  class SchedulerTestCase (line 18) | class SchedulerTestCase(unittest.TestCase):
    method setUp (line 19) | def setUp(self):
    method tearDown (line 55) | def tearDown(self):
    method test_push_items_to_queue (line 60) | def test_push_items_to_queue(self):
    method test_push_item_to_queue (line 90) | def test_push_item_to_queue(self):
    method test_push_item_to_queue_create_schedule_false (line 114) | def test_push_item_to_queue_create_schedule_false(self):
    method test_push_item_to_queue_full (line 140) | def test_push_item_to_queue_full(self):
    method test_push_item_to_queue_invalid (line 159) | def test_push_item_to_queue_invalid(self):
    method test_pop_item_from_queue (line 170) | def test_pop_item_from_queue(self):
    method test_post_push (line 190) | def test_post_push(self):
    method test_post_push_schedule_enabled (line 225) | def test_post_push_schedule_enabled(self):
    method test_post_push_schedule_update_schedule (line 259) | def test_post_push_schedule_update_schedule(self):
    method test_post_push_schedule_is_not_none (line 289) | def test_post_push_schedule_is_not_none(self):
    method test_post_push_schedule_is_none (line 318) | def test_post_push_schedule_is_none(self):
    method test_post_push_schedule_auto_calculate_deadline (line 342) | def test_post_push_schedule_auto_calculate_deadline(self):
    method test_post_pop (line 369) | def test_post_pop(self):

FILE: mula/tests/integration/test_task_store.py
  class StoreTestCase (line 13) | class StoreTestCase(unittest.TestCase):
    method setUp (line 14) | def setUp(self):
    method tearDown (line 35) | def tearDown(self):
    method test_create_task (line 39) | def test_create_task(self):
    method test_get_tasks (line 44) | def test_get_tasks(self):
    method get_tasks_by_type (line 57) | def get_tasks_by_type(self):
    method test_get_tasks_by_hash (line 72) | def test_get_tasks_by_hash(self):
    method test_get_task (line 92) | def test_get_task(self):
    method test_get_latest_task_by_hash (line 103) | def test_get_latest_task_by_hash(self):
    method test_update_task (line 123) | def test_update_task(self):
    method test_cancel_task (line 136) | def test_cancel_task(self):
    method test_get_status_counts (line 148) | def test_get_status_counts(self):
    method test_get_status_count_per_hour (line 189) | def test_get_status_count_per_hour(self):

FILE: mula/tests/mocks/item.py
  class MockData (line 7) | class MockData(pydantic.BaseModel):
    method __init__ (line 15) | def __init__(self, **data: Any):
    method hash (line 22) | def hash(self) -> str:

FILE: mula/tests/mocks/listener.py
  class MockListener (line 6) | class MockListener(listeners.Listener):
    method listen (line 7) | def listen(self) -> None:
    method stop (line 10) | def stop(self) -> None:
  class MockRabbitMQ (line 14) | class MockRabbitMQ(listeners.RabbitMQ):

FILE: mula/tests/mocks/queue.py
  class MockPriorityQueue (line 6) | class MockPriorityQueue(PriorityQueue):
    method create_hash (line 7) | def create_hash(self, p_item: models.Task) -> str:

FILE: mula/tests/mocks/ranker.py
  class MockRanker (line 6) | class MockRanker(rankers.Ranker):
    method rank (line 7) | def rank(self, obj: Any) -> int:

FILE: mula/tests/mocks/scheduler.py
  class MockScheduler (line 10) | class MockScheduler(schedulers.Scheduler):
    method run (line 13) | def run(self) -> None:
    method _run (line 22) | def _run(self) -> None:

FILE: mula/tests/mocks/services.py
  class MockKatalogusService (line 4) | class MockKatalogusService:
    method __init__ (line 5) | def __init__(self):
    method get_organisation (line 8) | def get_organisation(self, org_id: str) -> models.Organisation:
    method get_organisations (line 12) | def get_organisations(self) -> list[models.Organisation]:
    method get_new_boefjes_by_org_id (line 16) | def get_new_boefjes_by_org_id(self, org_id: str) -> list[models.Boefje]:
    method flush_caches (line 20) | def flush_caches(self) -> None:

FILE: mula/tests/mocks/task.py
  class MockTask (line 7) | class MockTask(pydantic.BaseModel):
    method hash (line 12) | def hash(self):

FILE: mula/tests/simulation/test_simulation.py
  class SimulationTestCase (line 20) | class SimulationTestCase(unittest.TestCase):
    method setUp (line 21) | def setUp(self):
    method create_normalizer_scheduler_for_organisation (line 29) | def create_normalizer_scheduler_for_organisation(self, organisation):
    method create_boefje_scheduler_for_organisation (line 47) | def create_boefje_scheduler_for_organisation(self, organisation):
    method test_simulation_boefje_queue (line 69) | def test_simulation_boefje_queue(self, mock_create_tasks_for_oois, moc...
    method test_simulation_normalizer_queue (line 101) | def test_simulation_normalizer_queue(self, mock_get_latest_raw_data, m...

FILE: mula/tests/unit/test_filter.py
  class TestModel (line 14) | class TestModel(Base):
  class TestModelChild (line 26) | class TestModelChild(Base):
  class FilteringTestCase (line 47) | class FilteringTestCase(unittest.TestCase):
    method setUp (line 48) | def setUp(self):
    method tearDown (line 100) | def tearDown(self):
    method test_apply_filter_basic (line 104) | def test_apply_filter_basic(self):
    method test_apply_filter_nested_fields (line 123) | def test_apply_filter_nested_fields(self):
    method test_apply_filter_casting (line 140) | def test_apply_filter_casting(self):
    method test_apply_filter_empty_filter_request (line 159) | def test_apply_filter_empty_filter_request(self):
    method test_apply_filter_or (line 168) | def test_apply_filter_or(self):
    method test_apply_filter_and (line 186) | def test_apply_filter_and(self):
    method test_apply_filter_not (line 203) | def test_apply_filter_not(self):
    method test_apply_filter_numeric (line 214) | def test_apply_filter_numeric(self):
    method test_apply_filter_string (line 231) | def test_apply_filter_string(self):
    method test_apply_filter_boolean (line 249) | def test_apply_filter_boolean(self):
    method test_apply_filter_eq (line 268) | def test_apply_filter_eq(self):
    method test_apply_filter_json_eq (line 289) | def test_apply_filter_json_eq(self):
    method test_apply_filter_ne (line 316) | def test_apply_filter_ne(self):
    method test_apply_filter_json_ne (line 338) | def test_apply_filter_json_ne(self):
    method test_apply_filter_is (line 363) | def test_apply_filter_is(self):
    method test_apply_filter_is_not (line 374) | def test_apply_filter_is_not(self):
    method test_apply_filter_gt (line 384) | def test_apply_filter_gt(self):
    method test_apply_filter_json_gt (line 407) | def test_apply_filter_json_gt(self):
    method test_apply_filter_gte (line 428) | def test_apply_filter_gte(self):
    method test_apply_filter_json_gte (line 453) | def test_apply_filter_json_gte(self):
    method test_apply_filter_lt (line 478) | def test_apply_filter_lt(self):
    method test_apply_filter_json_lt (line 499) | def test_apply_filter_json_lt(self):
    method test_apply_filter_lte (line 520) | def test_apply_filter_lte(self):
    method test_apply_filter_json_lte (line 543) | def test_apply_filter_json_lte(self):
    method test_apply_filter_like (line 568) | def test_apply_filter_like(self):
    method test_apply_filter_json_like (line 579) | def test_apply_filter_json_like(self):
    method test_apply_filter_not_like (line 592) | def test_apply_filter_not_like(self):
    method test_apply_filter_json_not_like (line 604) | def test_apply_filter_json_not_like(self):
    method test_apply_filter_ilike (line 616) | def test_apply_filter_ilike(self):
    method test_apply_filter_json_ilike (line 627) | def test_apply_filter_json_ilike(self):
    method test_apply_filter_not_ilike (line 640) | def test_apply_filter_not_ilike(self):
    method test_apply_filter_json_not_ilike (line 652) | def test_apply_filter_json_not_ilike(self):
    method test_apply_filter_in (line 664) | def test_apply_filter_in(self):
    method test_apply_filter_json_in (line 676) | def test_apply_filter_json_in(self):
    method test_apply_filter_not_in (line 687) | def test_apply_filter_not_in(self):
    method test_apply_filter_json_not_in (line 700) | def test_apply_filter_json_not_in(self):
    method test_apply_filter_contains (line 712) | def test_apply_filter_contains(self):
    method test_apply_filter_json_contains (line 723) | def test_apply_filter_json_contains(self):
    method test_apply_filter_jsonb_contains (line 736) | def test_apply_filter_jsonb_contains(self):
    method test_apply_filter_jsonb_contains_list (line 747) | def test_apply_filter_jsonb_contains_list(self):
    method test_apply_filter_jsonb_contained_by_list (line 761) | def test_apply_filter_jsonb_contained_by_list(self):
    method test_apply_filter_related (line 776) | def test_apply_filter_related(self):
    method test_apply_filter_related_reversed (line 789) | def test_apply_filter_related_reversed(self):
    method test_apply_filter_related_and_nested__ (line 800) | def test_apply_filter_related_and_nested__(self):
    method test_apply_filter_related_and_nested_reversed (line 812) | def test_apply_filter_related_and_nested_reversed(self):

FILE: mula/tests/unit/test_queue.py
  class PriorityQueueTestCase (line 16) | class PriorityQueueTestCase(unittest.TestCase):
    method setUp (line 17) | def setUp(self) -> None:
    method tearDown (line 35) | def tearDown(self) -> None:
    method _check_queue_empty (line 39) | def _check_queue_empty(self):
    method test_push (line 42) | def test_push(self):
    method test_push_incorrect_item_type (line 54) | def test_push_incorrect_item_type(self):
    method test_push_invalid_item (line 65) | def test_push_invalid_item(self):
    method test_push_replace_not_allowed (line 77) | def test_push_replace_not_allowed(self):
    method test_push_replace_allowed (line 96) | def test_push_replace_allowed(self):
    method test_push_updates_not_allowed (line 116) | def test_push_updates_not_allowed(self):
    method test_push_updates_allowed (line 141) | def test_push_updates_allowed(self):
    method test_push_priority_updates_not_allowed (line 166) | def test_push_priority_updates_not_allowed(self):
    method test_push_priority_updates_allowed (line 192) | def test_push_priority_updates_allowed(self):
    method test_remove_item (line 217) | def test_remove_item(self):
    method test_push_maxsize_not_allowed (line 232) | def test_push_maxsize_not_allowed(self):
    method test_push_maxsize_allowed (line 257) | def test_push_maxsize_allowed(self):
    method test_push_maxsize_allowed_high_priority (line 287) | def test_push_maxsize_allowed_high_priority(self):
    method test_push_maxsize_not_allowed_low_priority (line 317) | def test_push_maxsize_not_allowed_low_priority(self):
    method test_pop (line 342) | def test_pop(self):
    method test_pop_with_lock (line 360) | def test_pop_with_lock(self):
    method test_pop_without_lock (line 416) | def test_pop_without_lock(self):
    method test_pop_highest_priority (line 473) | def test_pop_highest_priority(self):
    method test_is_item_on_queue (line 492) | def test_is_item_on_queue(self):
    method test_is_item_not_on_queue (line 503) | def test_is_item_not_on_queue(self):

FILE: mula/tests/unit/test_rankers.py
  class BoefjeRankerTestCase (line 8) | class BoefjeRankerTestCase(TestCase):
    method setUp (line 15) | def setUp(self):
    method _obj_with_latest_task (line 20) | def _obj_with_latest_task(self, modified_at: datetime) -> mock.Mock:
    method test_rank_new_task_returns_2 (line 27) | def test_rank_new_task_returns_2(self):
    method test_rank_falsy_latest_task_returns_2 (line 32) | def test_rank_falsy_latest_task_returns_2(self):
    method test_rank_task_just_ran_is_close_to_max_priority (line 38) | def test_rank_task_just_ran_is_close_to_max_priority(self):
    method test_rank_task_older_than_max_days_returns_3 (line 46) | def test_rank_task_older_than_max_days_returns_3(self):
    method test_rank_task_within_grace_period_returns_minus_one (line 50) | def test_rank_task_within_grace_period_returns_minus_one(self):
    method test_rank_decays_monotonically_over_time (line 57) | def test_rank_decays_monotonically_over_time(self):
  class BoefjeRankerTimeBasedTestCase (line 67) | class BoefjeRankerTimeBasedTestCase(TestCase):
    method test_rank_returns_creation_epoch (line 68) | def test_rank_returns_creation_epoch(self):
  class NormalizerRankerTestCase (line 75) | class NormalizerRankerTestCase(TestCase):
    method test_rank_returns_boefje_meta_ended_at_epoch (line 81) | def test_rank_returns_boefje_meta_ended_at_epoch(self):
    method test_rank_orders_older_raws_lower (line 87) | def test_rank_orders_older_raws_lower(self):

FILE: mula/tests/unit/test_utils.py
  class ExpiringDictTestCase (line 7) | class ExpiringDictTestCase(unittest.TestCase):
    method test_lifetime_expired (line 8) | def test_lifetime_expired(self):
    method test_lifetime_not_expired (line 14) | def test_lifetime_not_expired(self):
    method test_toggle_expire (line 20) | def test_toggle_expire(self):

FILE: mula/tests/utils/functions.py
  class TestModel (line 14) | class TestModel(pydantic.BaseModel):
    method __init__ (line 22) | def __init__(self, **data: Any):
    method hash (line 29) | def hash(self) -> str:
  function create_test_model (line 33) | def create_test_model() -> TestModel:
  function create_task_push (line 37) | def create_task_push(priority: int, organisation: str, data: TestModel |...
  function create_task_push_dict (line 44) | def create_task_push_dict(priority: int, organisation: str, data: TestMo...
  function create_schedule (line 48) | def create_schedule(scheduler_id: str, data: Any | None = None) -> model...
  function create_task (line 53) | def create_task(scheduler_id: str, organisation: str, priority: int = 0,...
  function create_boefje (line 67) | def create_boefje() -> models.Boefje:
  function compile_query (line 73) | def compile_query(query: Query) -> str:

FILE: mula/tests/utils/json.py
  class UUIDEncoder (line 5) | class UUIDEncoder(json.JSONEncoder):
    method default (line 6) | def default(self, obj):

FILE: mula/tests/utils/memory.py
  function get_process_memory (line 7) | def get_process_memory():
  function profile_memory (line 13) | def profile_memory(func, *args, **kwargs):

FILE: octopoes/.ci/mock_bits/url_classification_mock/url_classification_mock.py
  function run (line 10) | def run(url: URL, additional_oois: list, config: dict) -> Iterator[OOI]:

FILE: octopoes/bits/ask_disallowed_domains/ask_disallowed_domains.py
  function run (line 11) | def run(input_ooi: Network, additional_oois: list, config: dict[str, Any...

FILE: octopoes/bits/ask_port_specification/ask_port_specification.py
  function run (line 11) | def run(input_ooi: Network, additional_oois: list, config: dict[str, Any...

FILE: octopoes/bits/ask_url_params_to_ignore/ask_url_params_to_ignore.py
  function run (line 11) | def run(input_ooi: Network, additional_oois: list, config: dict[str, Any...

FILE: octopoes/bits/check_csp_header/check_csp_header.py
  function is_xss_capable (line 18) | def is_xss_capable(content_type: str) -> bool:
  function run (line 24) | def run(resource: HTTPResource, additional_oois: list[HTTPHeader], confi...
  function _ip_valid (line 133) | def _ip_valid(source: str) -> bool:
  function _create_kat_finding (line 146) | def _create_kat_finding(header: Reference, kat_id: str, description: str...
  function _source_valid (line 152) | def _source_valid(policy: list[str]) -> bool:

FILE: octopoes/bits/check_cve_2021_41773/check_cve_2021_41773.py
  function run (line 9) | def run(input_ooi: HTTPHeader, additional_oois: list, config: dict[str, ...

FILE: octopoes/bits/check_hsts_header/check_hsts_header.py
  function run (line 10) | def run(input_ooi: HTTPHeader, additional_oois: list, config: dict[str, ...
  function _create_kat_finding (line 41) | def _create_kat_finding(header: Reference, kat_id: str, description: str...

FILE: octopoes/bits/cipher_classification/cipher_classification.py
  function get_severity_and_reasons (line 17) | def get_severity_and_reasons(cipher_suite: str) -> list[tuple[str, str]]:
  function get_highest_severity_and_all_reasons (line 47) | def get_highest_severity_and_all_reasons(cipher_suites: dict) -> tuple[s...
  function run (line 80) | def run(input_ooi: TLSCipher, additional_oois: list, config: dict[str, A...

FILE: octopoes/bits/default_findingtype_risk/default_findingtype_risk.py
  function run (line 8) | def run(input_ooi: FindingType, additional_oois: list, config: dict[str,...

FILE: octopoes/bits/definitions.py
  class BitParameterDefinition (line 17) | class BitParameterDefinition(BaseModel):
  class BitDefinition (line 22) | class BitDefinition(BaseModel):
  function get_bit_definitions (line 33) | def get_bit_definitions() -> dict[str, BitDefinition]:

FILE: octopoes/bits/disallowed_csp_hostnames/disallowed_csp_hostnames.py
  function get_disallowed_hostnames_from_config (line 13) | def get_disallowed_hostnames_from_config(config, config_key, default):
  function run (line 20) | def run(input_ooi: HTTPHeaderHostname, additional_oois: list, config: di...

FILE: octopoes/bits/dns_alias_resolving/dns_alias_resolving.py
  function run (line 10) | def run(

FILE: octopoes/bits/dns_resolving/dns_resolving.py
  function run (line 9) | def run(hostname: Hostname, additional_oois: list[DNSARecord | DNSAAAARe...

FILE: octopoes/bits/domain_owner_verification/domain_owner_verification.py
  function run (line 14) | def run(nameserver_record: DNSNSRecord, additional_oois, config: dict[st...

FILE: octopoes/bits/expiring_certificate/expiring_certificate.py
  function run (line 13) | def run(input_ooi: X509Certificate, additional_oois: list[Website], conf...

FILE: octopoes/bits/https_availability/https_availability.py
  function run (line 10) | def run(input_ooi: IPAddress, additional_oois: list[IPPort | Website], c...

FILE: octopoes/bits/https_redirect/https_redirect.py
  function run (line 9) | def run(input_ooi: HostnameHTTPURL, additional_oois: list[HTTPHeader], c...

FILE: octopoes/bits/internetnl/internetnl.py
  function run (line 10) | def run(input_ooi: Hostname, additional_oois: list[Finding | Website], c...

FILE: octopoes/bits/ipv6_nameservers/ipv6_nameservers.py
  function run (line 10) | def run(hostname: Hostname, additional_oois: list[DNSAAAARecord | DNSARe...

FILE: octopoes/bits/ipv6_webservers/ipv6_webservers.py
  function run (line 9) | def run(

FILE: octopoes/bits/missing_caa/missing_caa.py
  function run (line 12) | def run(input_ooi: Hostname, additional_oois: list[DNSCAARecord | NXDOMA...

FILE: octopoes/bits/missing_dkim/missing_dkim.py
  function run (line 13) | def run(input_ooi: Hostname, additional_oois: list[DKIMExists | NXDOMAIN...

FILE: octopoes/bits/missing_dmarc/missing_dmarc.py
  function run (line 13) | def run(input_ooi: Hostname, additional_oois: list[DMARCTXTRecord | NXDO...

FILE: octopoes/bits/missing_headers/missing_headers.py
  function is_xss_capable (line 21) | def is_xss_capable(content_type: str) -> bool:
  function run (line 27) | def ru
Copy disabled (too large) Download .json
Condensed preview — 2492 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (16,409K chars).
[
  {
    "path": ".dockerignore",
    "chars": 162,
    "preview": "*/.venv\n*/.pytest_cache\n*/__pycache__\n*/.mypy_cache\n*/.parcel-cache\n*/.git\n*/.github\n*/node_modules\n*/packaging\n*/export"
  },
  {
    "path": ".env-defaults",
    "chars": 1660,
    "preview": "# Container entrypoints will run database migrations if set to \"true\"\nDATABASE_MIGRATION=true\n\n# SECURITY WARNING: don't"
  },
  {
    "path": ".env-dist",
    "chars": 2127,
    "preview": "# === Notes === #\n\n# {% ... } values are generated dynamically for security reasons when this file is copied through `ma"
  },
  {
    "path": ".env-prod",
    "chars": 1256,
    "preview": "# `.env` overrides variables from `.env-prod` if included\n\n# If you use docker-compose.release-example.yml as base for a"
  },
  {
    "path": ".gitattributes",
    "chars": 706,
    "preview": "# These settings are for any web project\n\n# Handle line endings automatically for files detected as text\n# and leave all"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 39,
    "preview": "* @hasecon @cookiemonster @underdarknl\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/.gitignore",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 986,
    "preview": "---\nname: Bug report\nabout: Create a bug report to help us improve\ntitle: \"\"\nlabels: \"bug\"\nassignees: \"\"\n---\n\n_Please ad"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 817,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: KAT coordination repository\n    url: https://github.com/minvws/nl-k"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 1680,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"\"\nlabels: \"feature\"\nassignees: \"\"\n---\n\n_Please"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/user_story.md",
    "chars": 348,
    "preview": "---\nname: User story\nabout: Create a task that is phrased as a user story\ntitle: \"\"\nlabels: \"\"\nassignees: \"\"\n---\n\n## Use"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 695,
    "preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 2171,
    "preview": "### Changes\n\n_Please describe the essence of this PR in a few sentences. Mention any breaking changes or required config"
  },
  {
    "path": ".github/scripts/commit_sign_push.sh",
    "chars": 453,
    "preview": "#!/bin/bash\n\n#GITHUB_TOKEN should be ${{ secrets.GITHUB_TOKEN }}\n#DESTINATION_BRANCH should be ${{ github.ref }}\n\nFILES="
  },
  {
    "path": ".github/scripts/coverage_file_fixer.py",
    "chars": 1076,
    "preview": "#!/usr/bin/env python\n\n\n# This script fixes the filename attribute in a coverage.xml file for SonarCloud coverage analys"
  },
  {
    "path": ".github/workflows/boefjes-tests.yml",
    "chars": 2972,
    "preview": "name: Boefjes Test (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n\n    steps:\n    "
  },
  {
    "path": ".github/workflows/boefjes_container_image.yml",
    "chars": 2392,
    "preview": "name: Boefjes Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\""
  },
  {
    "path": ".github/workflows/boefjes_tests.yml",
    "chars": 1695,
    "preview": "name: Boefjes Run the test suite\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n   "
  },
  {
    "path": ".github/workflows/build-debian-docker-image.yml",
    "chars": 1752,
    "preview": "name: Create and publish Docker image for building Debian packages\n\non:\n  workflow_dispatch: {}\n  push:\n    branches:\n  "
  },
  {
    "path": ".github/workflows/build-rdo-package.yml",
    "chars": 11266,
    "preview": "name: Build RDO packages\n\nconcurrency:\n  group: ${{ github.ref }}\n  cancel-in-progress: true\n\non:\n  push:\n    tags:\n    "
  },
  {
    "path": ".github/workflows/build_docs_on_pr.yml",
    "chars": 1194,
    "preview": "name: Build docs artifact for PR\n\non:\n  pull_request:\n    paths:\n      # We generate documentation for boefje, bytes, mu"
  },
  {
    "path": ".github/workflows/bytes-tests.yml",
    "chars": 2097,
    "preview": "name: Bytes Tests (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n    steps:\n      "
  },
  {
    "path": ".github/workflows/bytes_container_image.yml",
    "chars": 2243,
    "preview": "name: Bytes Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n "
  },
  {
    "path": ".github/workflows/bytes_tests.yml",
    "chars": 895,
    "preview": "name: Bytes Run the test suite\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    p"
  },
  {
    "path": ".github/workflows/check_requirements.yml",
    "chars": 1144,
    "preview": "name: Check dependencies\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "chars": 1366,
    "preview": "name: \"CodeQL OpenKAT\"\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n  pull_request:\n  schedule:\n    # W"
  },
  {
    "path": ".github/workflows/containerized_boefjes.yml",
    "chars": 4941,
    "preview": "name: Build containerized boefjes\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n  "
  },
  {
    "path": ".github/workflows/debian_package.yml",
    "chars": 5438,
    "preview": "name: Debian packages\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n  pull_request"
  },
  {
    "path": ".github/workflows/deploy_docs.yml",
    "chars": 1420,
    "preview": "name: Compile and deploy documentation to Pages\n\non:\n  push:\n    branches:\n      - \"main\"\n\n  # Allows you to run this wo"
  },
  {
    "path": ".github/workflows/mula-tests.yml",
    "chars": 2086,
    "preview": "name: Mula Tests (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n    steps:\n      -"
  },
  {
    "path": ".github/workflows/mula_container_image.yml",
    "chars": 2234,
    "preview": "name: Mula Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n  "
  },
  {
    "path": ".github/workflows/mula_tests.yml",
    "chars": 1045,
    "preview": "name: Mula Run the test suite\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    pa"
  },
  {
    "path": ".github/workflows/octopoes-tests.yml",
    "chars": 2456,
    "preview": "name: Octopoes Tests (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n\n    steps:\n  "
  },
  {
    "path": ".github/workflows/octopoes_container_image.yml",
    "chars": 2268,
    "preview": "name: Octopoes Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*"
  },
  {
    "path": ".github/workflows/octopoes_rtest.yml",
    "chars": 1077,
    "preview": "name: Octopoes Run the robot framework integration tests\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n "
  },
  {
    "path": ".github/workflows/octopoes_tests.yml",
    "chars": 1189,
    "preview": "name: Octopoes tests\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n    "
  },
  {
    "path": ".github/workflows/pre_commit_checks.yml",
    "chars": 946,
    "preview": "name: Pre-commit checks\n\non:\n  workflow_call:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      -"
  },
  {
    "path": ".github/workflows/rocky-tests.yml",
    "chars": 2127,
    "preview": "name: Rocky Tests (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n\n    steps:\n     "
  },
  {
    "path": ".github/workflows/rocky_container_image.yml",
    "chars": 2307,
    "preview": "name: Rocky Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n "
  },
  {
    "path": ".github/workflows/rocky_makelang.yml",
    "chars": 1920,
    "preview": "name: Rocky Check if translations are up to date\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:"
  },
  {
    "path": ".github/workflows/rocky_tests.yml",
    "chars": 1214,
    "preview": "name: Rocky Run the test suite\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    p"
  },
  {
    "path": ".github/workflows/sonar-cloud.yml",
    "chars": 3367,
    "preview": "name: SonarCloud\n\non:\n  #workflow_dispatch:\n\n  #push:\n  #  branches:\n  #    - \"main\"\n  #pull_request:\n\njobs:\n  octopoes-"
  },
  {
    "path": ".github/workflows/test_debian_packages_on_ubuntu.yml",
    "chars": 12253,
    "preview": "name: Test installing the debian packages\n\non:\n  push:\n    tags:\n      - v*\n    branches:\n      - \"release**\"\n      - \"m"
  },
  {
    "path": ".gitignore",
    "chars": 7697,
    "preview": "\n# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n# Ed"
  },
  {
    "path": ".gitpod.yml",
    "chars": 133,
    "preview": "# Reference: https://www.gitpod.io/docs/references/gitpod-yml\n\ntasks:\n  - init: make\n\nports:\n  - port: 8000\n    onOpen: "
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 5532,
    "preview": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v5.0.0\n    hooks:\n      - id: trailing-whitespa"
  },
  {
    "path": ".stylelintrc.json",
    "chars": 575,
    "preview": "{\n  \"extends\": \"stylelint-config-standard-scss\",\n  \"ignoreFiles\": [\n    \"rocky/assets/css/themes/soft/fonts/tabler-icons"
  },
  {
    "path": "CONTRIBUTING.rst",
    "chars": 618,
    "preview": "============\nContributing\n============\n\nThank you, dear developer, who is considering to help out with OpenKAT! Feel wel"
  },
  {
    "path": "LICENSE",
    "chars": 13745,
    "preview": "                      EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                      EUPL © the European Union 2007, 2016\n\nT"
  },
  {
    "path": "Makefile",
    "chars": 6077,
    "preview": "SHELL := bash\n.ONESHELL:\n.NOTPARALLEL:\n\n# use HIDE to run commands invisibly, unless VERBOSE defined\nHIDE:=$(if $(VERBOS"
  },
  {
    "path": "README.rst",
    "chars": 5339,
    "preview": "================\nWhat is OpenKAT?\n================\n\nOpenKAT aims to monitor, record and analyze the status of informatio"
  },
  {
    "path": "boefjes/.ci/docker-compose.yml",
    "chars": 2712,
    "preview": "services:\n  katalogus_integration:\n    build:\n      context: ..\n      dockerfile: boefjes/Dockerfile\n      args:\n       "
  },
  {
    "path": "boefjes/.dockerignore",
    "chars": 377,
    "preview": "**/__pycache__\n**/*.pyc\n**/*.pyo\n**/*.pyd\n.Python\nenv\n.venv\npip-log.txt\npip-delete-this-directory.txt\n.tox\n.coverage.*\n."
  },
  {
    "path": "boefjes/.editorconfig",
    "chars": 265,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_sty"
  },
  {
    "path": "boefjes/.gitignore",
    "chars": 7518,
    "preview": "\n# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n# Ed"
  },
  {
    "path": "boefjes/Dockerfile",
    "chars": 1000,
    "preview": "ARG PYTHON_VERSION=3.13\nFROM python:$PYTHON_VERSION AS dev\n\nARG USER_UID=1000\nARG USER_GID=1000\n\nENTRYPOINT [\"/app/boefj"
  },
  {
    "path": "boefjes/LICENSE",
    "chars": 13745,
    "preview": "                      EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                      EUPL © the European Union 2007, 2016\n\nT"
  },
  {
    "path": "boefjes/MANIFEST.in",
    "chars": 308,
    "preview": "include README.md\ninclude LICENSE\ninclude boefjes/alembic.ini\n\nrecursive-include boefjes/katalogus/static *\nrecursive-in"
  },
  {
    "path": "boefjes/Makefile",
    "chars": 6335,
    "preview": "SHELL := bash\n.ONESHELL:\n.SHELLFLAGS := -eu -o pipefail -c\n.DELETE_ON_ERROR:\nMAKEFLAGS += --warn-undefined-variables\nMAK"
  },
  {
    "path": "boefjes/boefjes/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/__main__.py",
    "chars": 3351,
    "preview": "import click\nimport structlog\nfrom sqlalchemy.orm import sessionmaker\n\nfrom boefjes.clients.scheduler_client import Sche"
  },
  {
    "path": "boefjes/boefjes/alembic.ini",
    "chars": 2894,
    "preview": "# A generic, single database configuration.\n\n[alembic]\n# path to migration scripts\nscript_location = boefjes/migrations\n"
  },
  {
    "path": "boefjes/boefjes/api.py",
    "chars": 5459,
    "preview": "import multiprocessing\nimport uuid\nfrom datetime import datetime, timezone\nfrom multiprocessing.context import ForkConte"
  },
  {
    "path": "boefjes/boefjes/clients/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/clients/bytes_client.py",
    "chars": 5861,
    "preview": "import typing\nimport uuid\nfrom base64 import b64encode\nfrom collections.abc import Callable\nfrom functools import wraps\n"
  },
  {
    "path": "boefjes/boefjes/clients/scheduler_client.py",
    "chars": 7349,
    "preview": "import datetime\nimport os\nimport uuid\nfrom functools import cache\nfrom typing import Any\n\nimport httpx\nimport structlog\n"
  },
  {
    "path": "boefjes/boefjes/config.py",
    "chars": 6850,
    "preview": "import logging\nimport os\nfrom enum import Enum\nfrom pathlib import Path\nfrom typing import Any, Literal\n\nfrom pydantic i"
  },
  {
    "path": "boefjes/boefjes/dependencies/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/dependencies/encryption.py",
    "chars": 1337,
    "preview": "import abc\nimport base64\n\nfrom nacl.public import Box, PrivateKey, PublicKey\n\n\nclass EncryptMiddleware(abc.ABC):\n    @ab"
  },
  {
    "path": "boefjes/boefjes/dependencies/plugins.py",
    "chars": 10052,
    "preview": "from collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import Literal\n\nimport structlog\nfrom fastapi "
  },
  {
    "path": "boefjes/boefjes/job_handler.py",
    "chars": 10511,
    "preview": "import os\nfrom collections.abc import Callable\nfrom datetime import datetime, timezone\nfrom typing import Literal, cast\n"
  },
  {
    "path": "boefjes/boefjes/katalogus/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/katalogus/configs.py",
    "chars": 1103,
    "preview": "import structlog\nfrom fastapi import APIRouter, Depends\n\nfrom boefjes.config import settings\nfrom boefjes.dependencies.p"
  },
  {
    "path": "boefjes/boefjes/katalogus/organisations.py",
    "chars": 1611,
    "preview": "from fastapi import APIRouter, Depends, HTTPException, status\n\nfrom boefjes.sql.db import ObjectNotFoundException\nfrom b"
  },
  {
    "path": "boefjes/boefjes/katalogus/plugins.py",
    "chars": 9426,
    "preview": "import datetime\n\nimport structlog\nfrom croniter import croniter\nfrom fastapi import APIRouter, Body, Depends, HTTPExcept"
  },
  {
    "path": "boefjes/boefjes/katalogus/root.py",
    "chars": 3558,
    "preview": "from typing import Any\n\nimport structlog\nfrom fastapi import APIRouter, FastAPI, Request, status\nfrom fastapi.responses "
  },
  {
    "path": "boefjes/boefjes/katalogus/settings.py",
    "chars": 988,
    "preview": "from fastapi import APIRouter, Depends\n\nfrom boefjes.dependencies.plugins import PluginService, get_plugin_service\n\nrout"
  },
  {
    "path": "boefjes/boefjes/katalogus/version.py",
    "chars": 34,
    "preview": "__version__ = \"0.0.1-development\"\n"
  },
  {
    "path": "boefjes/boefjes/local/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/local/runner.py",
    "chars": 3035,
    "preview": "from collections.abc import Iterable\n\nimport structlog\n\nfrom boefjes.normalizer_interfaces import NormalizerJobRunner\nfr"
  },
  {
    "path": "boefjes/boefjes/logging.json",
    "chars": 512,
    "preview": "{\n  \"version\": 1,\n  \"disable_existing_loggers\": 0,\n  \"formatters\": {\n    \"default\": {\n      \"format\": \"%(message)s\"\n    "
  },
  {
    "path": "boefjes/boefjes/logging.py",
    "chars": 1060,
    "preview": "import json\nimport logging.config\n\nimport structlog\n\nfrom boefjes.config import settings\n\nwith settings.log_cfg.open() a"
  },
  {
    "path": "boefjes/boefjes/migrations/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/migrations/env.py",
    "chars": 2102,
    "preview": "from logging.config import fileConfig\n\nfrom alembic import context\nfrom sqlalchemy import engine_from_config, pool\n\nfrom"
  },
  {
    "path": "boefjes/boefjes/migrations/script.py.mako",
    "chars": 510,
    "preview": "\"\"\"${message}\n\nRevision ID: ${up_revision}\nRevises: ${down_revision | comma,n}\nCreate Date: ${create_date}\n\n\"\"\"\nfrom ale"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/0001_add_katalogus_models.py",
    "chars": 2799,
    "preview": "\"\"\"Add KATalogus models.\n\nRevision ID: 0001\nRevises:\nCreate Date: 2022-05-18 14:19:15.882901\n\n\"\"\"\n\nimport sqlalchemy as "
  },
  {
    "path": "boefjes/boefjes/migrations/versions/0002_change_lengths_of_several_char_fields.py",
    "chars": 2192,
    "preview": "\"\"\"Change lengths of several Char fields\n\nRevision ID: 0002\nRevises: 0001\nCreate Date: 2022-09-06 10:13:48.622901\n\n\"\"\"\n\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/0003_longer_plugin_ids.py",
    "chars": 1207,
    "preview": "\"\"\"Change lengths of several Char fields\n\nRevision ID: 0002\nRevises: 0001\nCreate Date: 2022-09-06 10:13:48.622901\n\n\"\"\"\n\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/197672984df0_make_organisation_code_field_larger.py",
    "chars": 847,
    "preview": "\"\"\"Make organisation code field larger\n\nRevision ID: 197672984df0\nRevises: 0003\nCreate Date: 2022-12-12 13:22:07.119970\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/5be152459a7b_introduce_schema_field_to_boefje_model.py",
    "chars": 1437,
    "preview": "\"\"\"Introduce schema field to Boefje model\n\nRevision ID: 5be152459a7b\nRevises: f9de6eb7824b\nCreate Date: 2024-08-08 14:47"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/6f99834a4a5a_introduce_boefje_and_normalizer_models.py",
    "chars": 2560,
    "preview": "\"\"\"Introduce Boefje and Normalizer models\n\nRevision ID: 6f99834a4a5a\nRevises: 7c88b9cd96aa\nCreate Date: 2024-05-28 13:00"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/7c88b9cd96aa_remove_the_repository_model.py",
    "chars": 3443,
    "preview": "\"\"\"remove the Repository model\n\nRevision ID: 7c88b9cd96aa\nRevises: cd34fdfafdaf\nCreate Date: 2024-05-22 06:48:40.788139\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/870fc302b852_remove_environment_keys_field.py",
    "chars": 1038,
    "preview": "\"\"\"Remove environment keys field\n\nRevision ID: 870fc302b852\nRevises: 5be152459a7b\nCreate Date: 2024-08-20 06:08:20.94392"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/9f48560b0000_add_schedule_interval_fields.py",
    "chars": 795,
    "preview": "\"\"\"Add cron field\n\nRevision ID: 9f48560b0000\nRevises: 870fc302b852\nCreate Date: 2024-09-18 13:12:40.926394\n\n\"\"\"\n\nimport "
  },
  {
    "path": "boefjes/boefjes/migrations/versions/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/migrations/versions/a2c8d54b0124_unique_plugin_names.py",
    "chars": 839,
    "preview": "\"\"\"Unique plugin names\n\nRevision ID: a2c8d54b0124\nRevises: 870fc302b852\nCreate Date: 2024-09-18 14:46:00.881022\n\n\"\"\"\n\nfr"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/cd34fdfafdaf_json_settings_for_settings_table.py",
    "chars": 4294,
    "preview": "\"\"\"Json settings for settings table\n\nRevision ID: cd34fdfafdaf\nRevises: 197672984df0\nCreate Date: 2023-02-16 14:47:20.42"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/f9de6eb7824b_introduce_boefjeconfig_model.py",
    "chars": 11240,
    "preview": "\"\"\"Introduce BoefjeConfig model\n\nRevision ID: f9de6eb7824b\nRevises: 6f99834a4a5a\nCreate Date: 2024-05-31 10:45:16.474714"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/fc0295b38184_add_run_on_field_to_boefje.py",
    "chars": 828,
    "preview": "\"\"\"Add run on field to boefje\n\nRevision ID: fc0295b38184\nRevises: 9f48560b0000\nCreate Date: 2025-02-04 16:43:59.171960\n\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/fdeaea4481b8_add_deduplication_flag.py",
    "chars": 874,
    "preview": "\"\"\"Add deduplication flag\n\nRevision ID: fdeaea4481b8\nRevises: fc0295b38184\nCreate Date: 2025-05-28 11:45:16.215166\n\n\"\"\"\n"
  },
  {
    "path": "boefjes/boefjes/normalizer_interfaces.py",
    "chars": 259,
    "preview": "from boefjes.normalizer_models import NormalizerResults\nfrom boefjes.worker.job_models import NormalizerMeta\n\n\nclass Nor"
  },
  {
    "path": "boefjes/boefjes/normalizer_models.py",
    "chars": 938,
    "preview": "from datetime import datetime\nfrom typing import Literal, TypeAlias\n\nfrom pydantic import BaseModel\n\nfrom octopoes.model"
  },
  {
    "path": "boefjes/boefjes/plugins/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/helpers.py",
    "chars": 337,
    "preview": "def cpe_to_name_version(cpe: str) -> tuple[str | None, str | None]:\n    \"\"\"Fetch the software name and version from a CP"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/adr_finding_types.json",
    "chars": 950,
    "preview": "{\n  \"API-NONSTANDARD-HTTP\": {\n    \"description\": \"Only apply standard HTTP methods\",\n    \"risk\": \"Recommendation\"\n  },\n "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/boefje.json",
    "chars": 380,
    "preview": "{\n  \"id\": \"adr-finding-types\",\n  \"name\": \"ADR Finding Types\",\n  \"description\": \"Hydrate information on API Design Rules "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/description.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/main.py",
    "chars": 285,
    "preview": "import json\n\nFINDING_TYPE_PATH = \"boefjes/plugins/kat_adr_finding_types/adr_finding_types.json\"\n\n\ndef run(boefje_meta: d"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/normalize.py",
    "chars": 1054,
    "preview": "import json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerAffirma"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/normalizer.json",
    "chars": 273,
    "preview": "{\n  \"id\": \"kat_adr_finding_types_normalize\",\n  \"name\": \"API Design Rules (ADR) Finding Types\",\n  \"description\": \"Parse A"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/boefje.Dockerfile",
    "chars": 287,
    "preview": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/dns-sec:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nCOPY --fr"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/boefje.Dockerfile.dockerignore",
    "chars": 103,
    "preview": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/boefje.json",
    "chars": 300,
    "preview": "{\n  \"id\": \"adr-validator\",\n  \"name\": \"API Design Rules validator\",\n  \"description\": \"Validate if an API conforms to the "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/description.md",
    "chars": 234,
    "preview": "# ADR validator\n\nBoefje to validate if an API conforms to the Dutch API Design Rules](https://publicatie.centrumvoorstan"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/main.py",
    "chars": 533,
    "preview": "import subprocess\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ooi = boefje_meta[\"arguments\""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/normalize.py",
    "chars": 988,
    "preview": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.m"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/normalizer.json",
    "chars": 411,
    "preview": "{\n  \"id\": \"adr-validator-normalize\",\n  \"name\": \"API Design Rules validator\",\n  \"description\": \"Parses and validates the "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_answer_parser/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_answer_parser/normalize.py",
    "chars": 379,
    "preview": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.m"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_answer_parser/normalizer.json",
    "chars": 288,
    "preview": "{\n  \"id\": \"kat_answer_parser\",\n  \"name\": \"Answer Parser\",\n  \"description\": \"Parses the answers from 'Config' objects. Co"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/boefje.json",
    "chars": 355,
    "preview": "{\n  \"id\": \"binaryedge\",\n  \"name\": \"BinaryEdge\",\n  \"description\": \"Use BinaryEdge to find open ports with vulnerabilities"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/containers/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/containers/normalize.py",
    "chars": 2169,
    "preview": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutpu"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/containers/normalizer.json",
    "chars": 425,
    "preview": "{\n  \"id\": \"kat_binaryedge_containers\",\n  \"name\": \"BinaryEdge containers\",\n  \"description\": \"Parse BinaryEdge data to che"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/databases/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/databases/normalize.py",
    "chars": 3142,
    "preview": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutpu"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/databases/normalizer.json",
    "chars": 487,
    "preview": "{\n  \"id\": \"kat_binaryedge_databases\",\n  \"name\": \"BinaryEdge databases\",\n  \"description\": \"Parses BinaryEdge data to chec"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/description.md",
    "chars": 394,
    "preview": "BinaryEdge essentially collects internet data and makes it searchable on their search engine, similar to Shodan and othe"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/http_web/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/http_web/normalize.py",
    "chars": 4234,
    "preview": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutpu"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/http_web/normalizer.json",
    "chars": 396,
    "preview": "{\n  \"id\": \"kat_binaryedge_http_web\",\n  \"name\": \"BinaryEdge Websites\",\n  \"description\": \"Parses BinaryEdge data to check "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/main.py",
    "chars": 1216,
    "preview": "import json\nimport math\nfrom os import getenv\n\nfrom pybinaryedge import BinaryEdge\n\n\ndef run(boefje_meta: dict) -> list["
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/message_queues/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/message_queues/normalize.py",
    "chars": 2016,
    "preview": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutpu"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/message_queues/normalizer.json",
    "chars": 446,
    "preview": "{\n  \"id\": \"kat_binaryedge_message_queues\",\n  \"name\": \"BinaryEdge message queues\",\n  \"description\": \"Parses BinaryEdge da"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/protocols/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/protocols/normalize.py",
    "chars": 5110,
    "preview": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutpu"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/protocols/normalizer.json",
    "chars": 459,
    "preview": "{\n  \"id\": \"kat_binaryedge_protocols\",\n  \"name\": \"BinaryEdge SSL/TLS protocols\",\n  \"description\": \"Parses BinaryEdge data"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/remote_desktop/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/remote_desktop/normalize.py",
    "chars": 2930,
    "preview": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutpu"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/remote_desktop/normalizer.json",
    "chars": 437,
    "preview": "{\n  \"id\": \"kat_binaryedge_remote_desktop\",\n  \"name\": \"Binary Edge remote desktop\",\n  \"description\": \"Parses BinaryEdge d"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/requirements.txt",
    "chars": 20,
    "preview": "pybinaryedge == 0.5\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/schema.json",
    "chars": 349,
    "preview": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"BINARYEDGE_API\": {\n      \"title\": \"BINARYEDGE_API\","
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/service_identification/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/service_identification/normalize.py",
    "chars": 4104,
    "preview": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutpu"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/service_identification/normalizer.json",
    "chars": 411,
    "preview": "{\n  \"id\": \"kat_binaryedge_service_identification\",\n  \"name\": \"BinaryEdge service identification\",\n  \"description\": \"Pars"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/services/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/services/normalize.py",
    "chars": 3331,
    "preview": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutpu"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/services/normalizer.json",
    "chars": 387,
    "preview": "{\n  \"id\": \"kat_binaryedge_services\",\n  \"name\": \"BinaryEdge services\",\n  \"description\": \"Parses BinaryEdge data to check "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_burpsuite/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_burpsuite/normalize.py",
    "chars": 5371,
    "preview": "import base64\nfrom collections.abc import Iterable\nfrom ipaddress import IPv4Address, IPv6Address, ip_address\nfrom urlli"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_burpsuite/normalizer.json",
    "chars": 345,
    "preview": "{\n  \"id\": \"kat_burpsuite_normalize\",\n  \"name\": \"Burpsuite normalizer\",\n  \"description\": \"Parses Burpsuite XML output int"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_calvin/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_calvin/normalize.py",
    "chars": 825,
    "preview": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerDeclaration, Normalize"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_calvin/normalizer.json",
    "chars": 215,
    "preview": "{\n  \"id\": \"calvin-normalize\",\n  \"name\": \"Calvin\",\n  \"description\": \"Produces applications and incidents for Calvin.\",\n  "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/boefje.json",
    "chars": 339,
    "preview": "{\n  \"id\": \"censys\",\n  \"name\": \"Censys\",\n  \"description\": \"Use Censys to discover open ports, services and certificates. "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/description.md",
    "chars": 837,
    "preview": "# Censys\n\nCensys is a search engine similar to Shodan. It continually scans the entire public IPv4 address space on 3,59"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/main.py",
    "chars": 272,
    "preview": "import json\n\nfrom censys.search import CensysHosts\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    h "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/normalize.py",
    "chars": 5366,
    "preview": "import json\nimport urllib.parse\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOu"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/normalizer.json",
    "chars": 411,
    "preview": "{\n  \"id\": \"kat_censys_normalize\",\n  \"name\": \"Censys\",\n  \"description\": \"Parses Cencys data into objects that can be used"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/requirements.txt",
    "chars": 16,
    "preview": "censys == 2.1.8\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/schema.json",
    "chars": 490,
    "preview": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"CENSYS_API_ID\": {\n      \"title\": \"CENSYS_API_ID\",\n "
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/boefje.json",
    "chars": 322,
    "preview": "{\n  \"id\": \"certificate-search\",\n  \"name\": \"CRT\",\n  \"description\": \"Searches for certificates and new hostnames in the tr"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/description.md",
    "chars": 381,
    "preview": "Certificate Transparency (CT) is an Internet security standard for monitoring and auditing the issuance of digital certi"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/main.py",
    "chars": 1611,
    "preview": "import json\n\nimport requests\n\nCRT_SH_API = \"https://crt.sh/\"\nMATCHES = (\"=\", \"ILIKE\", \"LIKE\", \"single\", \"any\", \"FTS\")\nSE"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/normalize.py",
    "chars": 2279,
    "preview": "import datetime\nimport json\nfrom collections.abc import Iterable\n\nfrom dateutil.parser import parse\n\nfrom boefjes.normal"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/normalizer.json",
    "chars": 313,
    "preview": "{\n  \"id\": \"kat_crt_sh_normalize\",\n  \"name\": \"Certificate Transparency logs (crt.sh)\",\n  \"description\": \"Parses data from"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/requirements.txt",
    "chars": 19,
    "preview": "requests == 2.33.0\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/boefje.json",
    "chars": 532,
    "preview": "{\n  \"id\": \"CVE-2023-34039\",\n  \"name\": \"CVE-2023-34039 - VMware Aria Operations\",\n  \"description\": \"Checks if there are s"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.0.0/id_rsa_vnera_keypair_6.0.0_platform",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAzbGeKAIbNI5h3LnQXhN3P1/8aUH9FfUQVaKKI/tOhzByQ/v4\nDKD5hfXl+oxkoGeqSafpccP"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.0.0/id_rsa_vnera_keypair_6.0.0_proxy",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAsgKBrNNWF+QwDEP1w4HNuVQNBwLU0g/7Ua3SNNxhQvgx9oe7\nOh6c8YFvtmpSIjpOj3aD+w0"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.1.0/id_rsa_vnera_keypair_6.1.0_platform",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAyYCxnpm+fPQmfJ9otzl6yBI5XbHQ0nLdod646tj48ZTnLAr/\nMSfHxpHmfJhavWbkOIPjMpE"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.1.0/id_rsa_vnera_keypair_6.1.0_proxy",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAt4VSizA9wlrjZiBVBhfsBjFopdcuR4t11TYovpBU+HzwzB0O\nGkoPxsju1ga6rWUDs7ubJD5"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.10.0/id_rsa_vnera_keypair_6.10.0_collector",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAtpYKEuuvwRmvleIjldsJLLW9k9GhJVE2te2vx1++P8L/Tkvt\nJWLP8zS/zYz/vQfSFoNxW0+"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.10.0/id_rsa_vnera_keypair_6.10.0_platform",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAzuQIqOXPnVopfWuERYwwycREObL4tiBpxyO4yzqPNP7mv04N\nPiFE+8sZhtecmP7DGn8BVPd"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.2.0/id_rsa_vnera_keypair_6.2.0_collector",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAxeui/xvc57I8Mkkku9qIc5mHIsUVlE1pWUapZlmLCiBHiYJx\nm8hZgWeJMfvuuIICn3UR4T1"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.2.0/id_rsa_vnera_keypair_6.2.0_platform",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEApLxS7fHHBNDLzvkK1TR2u1c3EETKbwzd6o5jMVIiC224pnIT\nS8CFfafoE3d2JRLNqwOfzm+"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.3.0/id_rsa_vnera_keypair_6.3.0_collector",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEApqXMad/xCg9JnXwb4QN1cJeJLrsYSTyN/BhkAOIWHJCmKAou\nOwG3jw9UwRd89Xsk7SH++oA"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.3.0/id_rsa_vnera_keypair_6.3.0_platform",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAtjqiOwCwrwL3Lmc3ZyXd3mme+2uWHqkxX0GmWrn0ObmoPC1d\nKWJqwAOdFvvIsdCGhUhiBHs"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.4.0/id_rsa_vnera_keypair_6.4.0_collector",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAzyAJX7j6Tg7ZVtXuzDl4yqFW5FM0X2ukzpI2JXH8UZge57PT\nn++Uukqbp9xvEHBaJmXUADm"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.4.0/id_rsa_vnera_keypair_6.4.0_platform",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA4VByn2KlKBikQkGqfcUyMGL8Kqgy34CcheX/rCG++bd5bRrj\nK3yy1fYj6AIYaUy8vegcfS0"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.5.0/id_rsa_vnera_keypair_6.5.0_collector",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAtW06onhEdfVRHvRVOUa/Z+Yw2/s5SVcdbqs8LgDYFUM18L+F\nog/JBqrN0nsVG/Ja5qjh3uE"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.5.0/id_rsa_vnera_keypair_6.5.0_platform",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAuW9ejickS1Uy7/rABgmdLVM7m2KCFfetbgDyWfAYEnrSByI5\nT3u+NCCC+M82vtEBDgakg6S"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.6.0/id_rsa_vnera_keypair_6.6.0_collector",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwyCSg+dXntVddVgHAvcuDbH+VsOuUztZqhiaeQtbQAXjpvxP\ncfznbIEyrgLSF6fG//Eii7O"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.6.0/id_rsa_vnera_keypair_6.6.0_platform",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwyCSg+dXntVddVgHAvcuDbH+VsOuUztZqhiaeQtbQAXjpvxP\ncfznbIEyrgLSF6fG//Eii7O"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.7.0/id_rsa_vnera_keypair_6.7.0_collector",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAyfqMG/j7J3dX3bXLD7b+K7Oma9viSjjpR1SqgDI3SghskVBw\n5hg0vnTyzwou0RgdnmLGpBt"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.7.0/id_rsa_vnera_keypair_6.7.0_platform",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA4Iv5cXgnFvDdiktZe7zAc9mmBKS8WeodaZteHKh1khyHFm7d\noRNnCWV9h2yY+4Wktp+BEF3"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.8.0/id_rsa_vnera_keypair_6.8.0_collector",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAwOk7AhHKSloJQjIQg4YB0XIK6Q7Yggu9lCg1PWnjLqJQDywP\n7X0DMElimJRG2FRqCh8QomQ"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.8.0/id_rsa_vnera_keypair_6.8.0_platform",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0h41gxtVvp/p62gUE+KrgD+8kOEDM75UDaDqZDw8VmNnjKDx\nVSR01+7732O4bwCY8iPRe+0"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.9.0/id_rsa_vnera_keypair_6.9.0_collector",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAoUJXpD/5Wh7r4GIKD9UseSse3XTmMoS6IhsgmEkathmwdTww\nqzxA4vDcDufewZV5Jb6ekCe"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.9.0/id_rsa_vnera_keypair_6.9.0_platform",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpgIBAAKCAQEA99Bv5dUKWoLUuE2CRiri7LazYVFqH09vOZwBXPc61arFiZaI\nIrdqMxrkQ1AuBSfMVjSdOFY"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/main.py",
    "chars": 2360,
    "preview": "\"\"\"\nFrom:\n\nhttps://github.com/sinsinology/CVE-2023-34039/blob/main/CVE-2023-34039.py\n\nVMWare Aria Operations for Network"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_35078/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_35078/boefje.json",
    "chars": 475,
    "preview": "{\n  \"id\": \"CVE_2023_35078\",\n  \"name\": \"CVE-2023-35078 - Ivanti EPMM\",\n  \"description\": \"Checks websites for the presents"
  }
]

// ... and 2292 more files (download for full content)

About this extraction

This page contains the full source code of the minvws/nl-kat-coordination GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 2492 files (28.6 MB), approximately 3.9M tokens, and a symbol index with 5266 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.

Copied to clipboard!