howler-api 2.10.0.dev131__tar.gz → 2.10.0.dev155__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/PKG-INFO +2 -2
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/__init__.py +37 -36
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/borealis.py +10 -9
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/app.py +12 -9
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/cronjobs/__init__.py +2 -1
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/collection.py +7 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/howler_store.py +25 -9
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/helper/search.py +8 -10
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/helper.py +4 -6
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/hit.py +0 -12
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/random_data.py +62 -4
- howler_api-2.10.0.dev155/howler/plugins/__init__.py +25 -0
- howler_api-2.10.0.dev155/howler/plugins/config.py +123 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/config_service.py +3 -3
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/notebook_service.py +5 -15
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/pyproject.toml +2 -2
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/README.md +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/add_label.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/add_to_bundle.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/change_field.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/demote.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/example_plugin.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/prioritization.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/promote.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/remove_from_bundle.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/remove_label.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/actions/transition.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/base.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/socket.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/action.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/analytic.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/auth.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/configs.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/dossier.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/help.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/hit.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/notebook.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/overview.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/search.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/template.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/tool.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/user.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/utils/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/utils/etag.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/api/v1/view.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/README.md +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/classification.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/classification.yml +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/exceptions.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/hexdump.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/iprange.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/loader.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/logging/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/logging/audit.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/logging/format.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/net.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/net_static.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/random_user.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/common/swagger.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/config.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/cronjobs/retention.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/cronjobs/rules.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/README.md +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/bulk.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/constants.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/exceptions.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/migrations/fix_process.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/operations.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/schemas.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/store.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/support/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/support/build.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/support/schemas.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/datastore/types.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/error.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/external/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/external/generate_mitre.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/external/generate_sigma_rules.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/external/generate_tlds.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/external/reindex_data.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/external/wipe_databases.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/gunicorn_config.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/healthz.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/helper/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/helper/azure.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/helper/discover.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/helper/hit.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/helper/oauth.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/helper/workflow.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/helper/ws.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/README.md +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/base.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/charter.txt +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/howler_enum.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/action.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/analytic.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/assemblyline.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/aws.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/azure.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/cbs.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/config.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/dossier.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/agent.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/autonomous_system.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/client.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/cloud.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/code_signature.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/container.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/dns.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/egress.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/elf.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/email.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/error.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/event.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/faas.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/file.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/geo.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/group.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/hash.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/host.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/http.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/ingress.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/interface.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/network.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/observer.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/organization.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/os.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/pe.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/process.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/registry.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/related.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/rule.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/server.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/threat.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/tls.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/url.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/user.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/user_agent.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/ecs/vulnerability.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/gcp.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/howler_data.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/lead.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/localized_label.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/overview.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/pivot.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/template.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/user.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/models/view.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/odm/randomizer.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/patched.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/README.md +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/counters.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/events.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/hash.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/lock.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/queues/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/queues/comms.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/queues/multi.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/queues/named.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/queues/priority.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/set.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/remote/datatypes/user_quota_tracker.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/security/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/security/socket.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/security/utils.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/action_service.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/analytic_service.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/auth_service.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/dossier_service.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/event_service.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/hit_service.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/jwt_service.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/lucene_service.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/services/user_service.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/__init__.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/annotations.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/chunk.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/dict_utils.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/isotime.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/list_utils.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/lucene.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/path.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/socket_utils.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/str_utils.py +0 -0
- {howler_api-2.10.0.dev131 → howler_api-2.10.0.dev155}/howler/utils/uid.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: howler-api
|
|
3
|
-
Version: 2.10.0.
|
|
3
|
+
Version: 2.10.0.dev155
|
|
4
4
|
Summary: Howler - API server
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
|
|
@@ -50,7 +50,7 @@ Requires-Dist: python-datemath (==3.0.3)
|
|
|
50
50
|
Requires-Dist: python-dotenv (>=1.1.0,<2.0.0)
|
|
51
51
|
Requires-Dist: pyyaml (==6.0.2)
|
|
52
52
|
Requires-Dist: redis (==4.5.4)
|
|
53
|
-
Requires-Dist: requests (==2.32.
|
|
53
|
+
Requires-Dist: requests (==2.32.4)
|
|
54
54
|
Requires-Dist: typing-extensions (>=4.12.2,<5.0.0)
|
|
55
55
|
Requires-Dist: validators (>=0.34.0,<0.35.0)
|
|
56
56
|
Requires-Dist: wsproto (==1.2.0)
|
|
@@ -5,8 +5,8 @@ from pathlib import Path
|
|
|
5
5
|
from typing import Any, Optional
|
|
6
6
|
|
|
7
7
|
from howler.common.logging import get_logger
|
|
8
|
-
from howler.config import config
|
|
9
8
|
from howler.odm.models.user import User
|
|
9
|
+
from howler.plugins import get_plugins
|
|
10
10
|
|
|
11
11
|
logger = get_logger(__file__)
|
|
12
12
|
|
|
@@ -89,20 +89,25 @@ def execute(
|
|
|
89
89
|
Returns:
|
|
90
90
|
list[dict[str, Any]]: A report on the execution
|
|
91
91
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
92
|
+
operation = None
|
|
93
|
+
try:
|
|
94
|
+
operation = importlib.import_module(f"howler.actions.{operation_id}")
|
|
95
|
+
except ImportError:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
if not operation:
|
|
99
|
+
for plugin in get_plugins():
|
|
100
|
+
if not plugin.modules.operations:
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
operation = next(
|
|
104
|
+
(operation for operation in plugin.modules.operations if operation.OPERATION_ID == operation_id), None
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if operation:
|
|
108
|
+
break
|
|
109
|
+
|
|
110
|
+
if not operation:
|
|
106
111
|
return [
|
|
107
112
|
{
|
|
108
113
|
"query": query,
|
|
@@ -112,7 +117,7 @@ def execute(
|
|
|
112
117
|
}
|
|
113
118
|
]
|
|
114
119
|
|
|
115
|
-
missing_roles = set(
|
|
120
|
+
missing_roles = set(operation.specification()["roles"]) - set(user["type"])
|
|
116
121
|
if missing_roles:
|
|
117
122
|
return [
|
|
118
123
|
{
|
|
@@ -126,7 +131,7 @@ def execute(
|
|
|
126
131
|
}
|
|
127
132
|
]
|
|
128
133
|
|
|
129
|
-
report =
|
|
134
|
+
report = operation.execute(query=query, request_id=request_id, user=user, **kwargs)
|
|
130
135
|
|
|
131
136
|
return __sanitize_report(report)
|
|
132
137
|
|
|
@@ -139,28 +144,24 @@ def specifications() -> list[dict[str, Any]]:
|
|
|
139
144
|
"""
|
|
140
145
|
specifications = []
|
|
141
146
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
for module in (
|
|
148
|
+
_file
|
|
149
|
+
for _file in Path(__file__).parent.iterdir()
|
|
150
|
+
if _file.suffix == ".py" and _file.name not in ["__init__.py", "example_plugin.py"]
|
|
151
|
+
):
|
|
152
|
+
try:
|
|
153
|
+
operation = importlib.import_module(f"howler.actions.{module.stem}")
|
|
148
154
|
|
|
149
|
-
|
|
150
|
-
for module in (
|
|
151
|
-
_file
|
|
152
|
-
for _file in module_path.iterdir()
|
|
153
|
-
if _file.suffix == ".py" and _file.name not in ["__init__.py", "example_plugin.py"]
|
|
154
|
-
):
|
|
155
|
-
try:
|
|
156
|
-
automation = importlib.import_module(f"{module_name}.actions.{module.stem}")
|
|
155
|
+
specifications.append(__sanitize_specification(operation.specification()))
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
except Exception: # pragma: no cover
|
|
158
|
+
logger.exception("Error when initializing %s", module)
|
|
160
159
|
|
|
161
|
-
|
|
160
|
+
for plugin in get_plugins():
|
|
161
|
+
if not plugin.modules.operations:
|
|
162
|
+
continue
|
|
162
163
|
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
for operation in plugin.modules.operations:
|
|
165
|
+
specifications.append(__sanitize_specification(operation.specification()))
|
|
165
166
|
|
|
166
167
|
return specifications
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import sys
|
|
2
2
|
import time
|
|
3
3
|
from typing import Callable, Optional
|
|
4
4
|
|
|
@@ -11,6 +11,7 @@ from howler.common.exceptions import AuthenticationException
|
|
|
11
11
|
from howler.common.logging import get_logger
|
|
12
12
|
from howler.common.swagger import generate_swagger_docs
|
|
13
13
|
from howler.config import cache, config
|
|
14
|
+
from howler.plugins import get_plugins
|
|
14
15
|
from howler.security import api_login
|
|
15
16
|
|
|
16
17
|
SUB_API = "borealis"
|
|
@@ -20,19 +21,19 @@ borealis_api._doc = "Proxy enrichment requests to borealis"
|
|
|
20
21
|
logger = get_logger(__file__)
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
def skip_cache(*args):
|
|
25
|
+
"Function to skip cache in testing mode"
|
|
26
|
+
return "pytest" in sys.modules
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@cache.memoize(15 * 60, unless=skip_cache)
|
|
24
30
|
def get_token(access_token: str) -> str:
|
|
25
31
|
"""Get a borealis token based on the current howler token"""
|
|
26
32
|
get_borealis_token: Optional[Callable[[str], str]] = None
|
|
27
33
|
|
|
28
|
-
for plugin in
|
|
29
|
-
|
|
30
|
-
module = importlib.import_module(f"{plugin}.token.borealis")
|
|
31
|
-
|
|
32
|
-
get_borealis_token = module.get_borealis_token
|
|
34
|
+
for plugin in get_plugins():
|
|
35
|
+
if get_borealis_token := plugin.modules.token_functions.get("borealis", None):
|
|
33
36
|
break
|
|
34
|
-
except ImportError:
|
|
35
|
-
logger.info("Plugin %s does not modify the borealis access token.")
|
|
36
37
|
|
|
37
38
|
if get_borealis_token:
|
|
38
39
|
borealis_access_token = get_borealis_token(access_token)
|
|
@@ -4,20 +4,22 @@ from pathlib import Path
|
|
|
4
4
|
|
|
5
5
|
from dotenv import load_dotenv
|
|
6
6
|
|
|
7
|
-
from howler.
|
|
7
|
+
from howler.plugins import get_plugins
|
|
8
8
|
|
|
9
9
|
load_dotenv()
|
|
10
10
|
|
|
11
11
|
# We append the plugin directory for howler to the python part
|
|
12
12
|
PLUGIN_PATH = Path(os.environ.get("HWL_PLUGIN_DIRECTORY", "/etc/howler/plugins"))
|
|
13
13
|
sys.path.insert(0, str(PLUGIN_PATH))
|
|
14
|
+
|
|
15
|
+
from howler.odm.models.config import config
|
|
16
|
+
|
|
14
17
|
if config.ui.debug and PLUGIN_PATH.exists():
|
|
15
18
|
for _plugin in PLUGIN_PATH.iterdir():
|
|
16
19
|
sys.path.append(
|
|
17
20
|
str(Path(os.path.realpath(_plugin)) / f"../.venv/lib/python3.{sys.version_info.minor}/site-packages")
|
|
18
21
|
)
|
|
19
22
|
|
|
20
|
-
import importlib
|
|
21
23
|
import logging
|
|
22
24
|
from typing import Any, cast
|
|
23
25
|
|
|
@@ -142,13 +144,14 @@ if HWL_USE_REST_API or DEBUG:
|
|
|
142
144
|
logger.debug("Enabled Borealis Integration")
|
|
143
145
|
app.register_blueprint(borealis_api)
|
|
144
146
|
|
|
145
|
-
for
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
logger.info("
|
|
147
|
+
logger.info("Checking plugins for additional routes")
|
|
148
|
+
for plugin in get_plugins():
|
|
149
|
+
if not plugin.modules.routes:
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
for route in cast(list[Blueprint], plugin.modules.routes):
|
|
153
|
+
logger.info("Enabling additional endpoint: %s", route.url_prefix)
|
|
154
|
+
app.register_blueprint(route)
|
|
152
155
|
|
|
153
156
|
|
|
154
157
|
else:
|
|
@@ -19,6 +19,7 @@ from datemath.helpers import DateMathException
|
|
|
19
19
|
from howler import odm
|
|
20
20
|
from howler.common.exceptions import HowlerRuntimeError, HowlerValueError, NonRecoverableError
|
|
21
21
|
from howler.common.loader import APP_NAME
|
|
22
|
+
from howler.common.logging.format import HWL_DATE_FORMAT, HWL_LOG_FORMAT
|
|
22
23
|
from howler.datastore.bulk import ElasticBulkPlan
|
|
23
24
|
from howler.datastore.constants import BACK_MAPPING, TYPE_MAPPING
|
|
24
25
|
from howler.datastore.exceptions import (
|
|
@@ -58,6 +59,12 @@ if typing.TYPE_CHECKING:
|
|
|
58
59
|
TRANSPORT_TIMEOUT = int(environ.get("HWL_DATASTORE_TRANSPORT_TIMEOUT", "10"))
|
|
59
60
|
|
|
60
61
|
logger = logging.getLogger("howler.api.datastore")
|
|
62
|
+
logger.setLevel(logging.INFO)
|
|
63
|
+
console = logging.StreamHandler()
|
|
64
|
+
console.setLevel(logging.INFO)
|
|
65
|
+
console.setFormatter(logging.Formatter(HWL_LOG_FORMAT, HWL_DATE_FORMAT))
|
|
66
|
+
logger.addHandler(console)
|
|
67
|
+
|
|
61
68
|
ModelType = TypeVar("ModelType", bound=Model)
|
|
62
69
|
write_block_settings = {"settings": {"index.blocks.write": True}}
|
|
63
70
|
write_unblock_settings = {"settings": {"index.blocks.write": None}}
|
|
@@ -14,23 +14,39 @@ from howler.odm.models.overview import Overview
|
|
|
14
14
|
from howler.odm.models.template import Template
|
|
15
15
|
from howler.odm.models.user import User
|
|
16
16
|
from howler.odm.models.view import View
|
|
17
|
+
from howler.plugins import get_plugins
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
20
|
from howler.datastore.store import ESStore
|
|
20
21
|
|
|
22
|
+
INDEXES = [
|
|
23
|
+
("hit", Hit),
|
|
24
|
+
("template", Template),
|
|
25
|
+
("overview", Overview),
|
|
26
|
+
("analytic", Analytic),
|
|
27
|
+
("action", Action),
|
|
28
|
+
("user", User),
|
|
29
|
+
("view", View),
|
|
30
|
+
("dossier", Dossier),
|
|
31
|
+
("user_avatar", None),
|
|
32
|
+
]
|
|
33
|
+
|
|
21
34
|
|
|
22
35
|
class HowlerDatastore(object):
|
|
23
36
|
def __init__(self, datastore_object: "ESStore"):
|
|
24
37
|
self.ds = datastore_object
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
|
|
39
|
+
for plugin in get_plugins():
|
|
40
|
+
for _index, _odm in INDEXES:
|
|
41
|
+
if _odm is None:
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
if modify_odm := plugin.modules.odm.modify_odm.get(_index):
|
|
45
|
+
logger.info("Modifying %s odm with function from plugin %s", _index, plugin.name)
|
|
46
|
+
modify_odm(_odm)
|
|
47
|
+
|
|
48
|
+
for _index, _odm in INDEXES:
|
|
49
|
+
self.ds.register(_index, _odm)
|
|
34
50
|
|
|
35
51
|
def __enter__(self):
|
|
36
52
|
return self
|
|
@@ -11,17 +11,15 @@ ADMIN_INDEX_MAP: dict[str, Callable[[], ESCollection]] = {}
|
|
|
11
11
|
|
|
12
12
|
ADMIN_INDEX_ORDER_MAP: dict[str, str] = {}
|
|
13
13
|
|
|
14
|
-
ds = datastore()
|
|
15
|
-
|
|
16
14
|
INDEX_MAP: dict[str, Callable[[], ESCollection]] = {
|
|
17
|
-
"action": lambda:
|
|
18
|
-
"analytic": lambda:
|
|
19
|
-
"dossier": lambda:
|
|
20
|
-
"hit": lambda:
|
|
21
|
-
"overview": lambda:
|
|
22
|
-
"template": lambda:
|
|
23
|
-
"user": lambda:
|
|
24
|
-
"view": lambda:
|
|
15
|
+
"action": lambda: datastore().action,
|
|
16
|
+
"analytic": lambda: datastore().analytic,
|
|
17
|
+
"dossier": lambda: datastore().dossier,
|
|
18
|
+
"hit": lambda: datastore().hit,
|
|
19
|
+
"overview": lambda: datastore().overview,
|
|
20
|
+
"template": lambda: datastore().template,
|
|
21
|
+
"user": lambda: datastore().user,
|
|
22
|
+
"view": lambda: datastore().view,
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
INDEX_ORDER_MAP: dict[str, str] = {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import importlib
|
|
2
1
|
import json
|
|
3
2
|
import random
|
|
4
3
|
import sys
|
|
@@ -28,6 +27,7 @@ from howler.odm.randomizer import (
|
|
|
28
27
|
random_department,
|
|
29
28
|
random_model_obj,
|
|
30
29
|
)
|
|
30
|
+
from howler.plugins import get_plugins
|
|
31
31
|
from howler.security.utils import get_password_hash
|
|
32
32
|
from howler.utils.uid import get_random_id
|
|
33
33
|
|
|
@@ -285,12 +285,10 @@ def generate_useful_hit(lookups: dict[str, dict[str, Any]], users: list[User], p
|
|
|
285
285
|
log.previous_version = get_random_id()
|
|
286
286
|
|
|
287
287
|
new_keys: list[str] = []
|
|
288
|
-
for plugin in
|
|
289
|
-
|
|
290
|
-
_new_keys, hit =
|
|
288
|
+
for plugin in get_plugins(): # pragma: no cover
|
|
289
|
+
if generate := plugin.modules.odm.generation.get("hit", None):
|
|
290
|
+
_new_keys, hit = generate(hit)
|
|
291
291
|
new_keys += _new_keys
|
|
292
|
-
except (ImportError, AttributeError):
|
|
293
|
-
logger.exception("Plugin does not expose useful hit generation")
|
|
294
292
|
|
|
295
293
|
if len(new_keys) > 0:
|
|
296
294
|
logger.debug("%s new top-level fields configured")
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# mypy: ignore-errors
|
|
2
|
-
import importlib
|
|
3
2
|
from typing import Optional
|
|
4
3
|
|
|
5
4
|
from howler import odm
|
|
6
5
|
from howler.common.logging import get_logger
|
|
7
|
-
from howler.config import config
|
|
8
6
|
from howler.odm.models.assemblyline import AssemblyLine
|
|
9
7
|
from howler.odm.models.aws import AWS
|
|
10
8
|
from howler.odm.models.azure import Azure
|
|
@@ -351,16 +349,6 @@ class Hit(odm.Model):
|
|
|
351
349
|
)
|
|
352
350
|
|
|
353
351
|
|
|
354
|
-
for plugin in config.core.plugins:
|
|
355
|
-
try:
|
|
356
|
-
importlib.import_module(f"{plugin}.odm.hit").modify_odm(Hit)
|
|
357
|
-
except (ImportError, AttributeError) as err:
|
|
358
|
-
if f"No module named '{plugin}'" in str(err):
|
|
359
|
-
raise
|
|
360
|
-
else:
|
|
361
|
-
logger.info("Plugin %s does not modify the ODM.", plugin)
|
|
362
|
-
|
|
363
|
-
|
|
364
352
|
if __name__ == "__main__":
|
|
365
353
|
from pprint import pprint
|
|
366
354
|
|
|
@@ -4,6 +4,8 @@ from pathlib import Path
|
|
|
4
4
|
|
|
5
5
|
from dotenv import load_dotenv
|
|
6
6
|
|
|
7
|
+
from howler.plugins import get_plugins
|
|
8
|
+
|
|
7
9
|
load_dotenv()
|
|
8
10
|
|
|
9
11
|
# We append the plugin directory for howler to the python part
|
|
@@ -50,6 +52,20 @@ logger = get_logger(__file__)
|
|
|
50
52
|
hit_helper = OdmHelper(Hit)
|
|
51
53
|
|
|
52
54
|
|
|
55
|
+
def run_modifications(odm: str, data: Any, log: bool = False):
|
|
56
|
+
"Running modifications"
|
|
57
|
+
new_keys: list[str] = []
|
|
58
|
+
for plugin in get_plugins(): # pragma: no cover
|
|
59
|
+
if generate := plugin.modules.odm.generation.get(odm, None):
|
|
60
|
+
_new_keys, data = generate(data)
|
|
61
|
+
new_keys += _new_keys
|
|
62
|
+
|
|
63
|
+
if len(new_keys) > 0 and log:
|
|
64
|
+
logger.debug("%s new top-level fields configured for %s", len(new_keys), odm)
|
|
65
|
+
|
|
66
|
+
return data
|
|
67
|
+
|
|
68
|
+
|
|
53
69
|
def create_users(ds):
|
|
54
70
|
"""Create number of user accounts"""
|
|
55
71
|
admin_pass = os.getenv("DEV_ADMIN_PASS", "admin") or "admin"
|
|
@@ -68,6 +84,9 @@ def create_users(ds):
|
|
|
68
84
|
"owner": "admin",
|
|
69
85
|
}
|
|
70
86
|
)
|
|
87
|
+
|
|
88
|
+
admin_view = run_modifications("view", admin_view)
|
|
89
|
+
|
|
71
90
|
user_data = User(
|
|
72
91
|
{
|
|
73
92
|
"apikeys": {
|
|
@@ -106,6 +125,9 @@ def create_users(ds):
|
|
|
106
125
|
"favourite_views": [admin_view.view_id],
|
|
107
126
|
}
|
|
108
127
|
)
|
|
128
|
+
|
|
129
|
+
user_data = run_modifications("view", user_data, True)
|
|
130
|
+
|
|
109
131
|
ds.user.save("admin", user_data)
|
|
110
132
|
ds.user_avatar.save(
|
|
111
133
|
"admin",
|
|
@@ -149,6 +171,10 @@ def create_users(ds):
|
|
|
149
171
|
"favourite_views": [user_view.view_id],
|
|
150
172
|
}
|
|
151
173
|
)
|
|
174
|
+
|
|
175
|
+
user_view = run_modifications("view", user_view)
|
|
176
|
+
user_data = run_modifications("user", user_data)
|
|
177
|
+
|
|
152
178
|
ds.user.save("user", user_data)
|
|
153
179
|
ds.user_avatar.save(
|
|
154
180
|
"user",
|
|
@@ -192,6 +218,10 @@ def create_users(ds):
|
|
|
192
218
|
"favourite_views": [huey_view.view_id],
|
|
193
219
|
}
|
|
194
220
|
)
|
|
221
|
+
|
|
222
|
+
huey_view = run_modifications("view", huey_view)
|
|
223
|
+
huey_data = run_modifications("user", huey_data)
|
|
224
|
+
|
|
195
225
|
ds.user.save("huey", huey_data)
|
|
196
226
|
ds.user_avatar.save(
|
|
197
227
|
"huey",
|
|
@@ -223,7 +253,9 @@ def create_users(ds):
|
|
|
223
253
|
}
|
|
224
254
|
)
|
|
225
255
|
|
|
226
|
-
|
|
256
|
+
shawnh_view = run_modifications("view", shawnh_view)
|
|
257
|
+
shawn_data = run_modifications("user", shawn_data)
|
|
258
|
+
|
|
227
259
|
ds.user.save("shawn-h", shawn_data)
|
|
228
260
|
ds.view.save(shawnh_view.view_id, shawnh_view)
|
|
229
261
|
|
|
@@ -251,7 +283,9 @@ def create_users(ds):
|
|
|
251
283
|
}
|
|
252
284
|
)
|
|
253
285
|
|
|
254
|
-
|
|
286
|
+
goose_view = run_modifications("view", goose_view)
|
|
287
|
+
goose_data = run_modifications("user", goose_data)
|
|
288
|
+
|
|
255
289
|
ds.user.save("goose", goose_data)
|
|
256
290
|
ds.view.save(goose_view.view_id, goose_view)
|
|
257
291
|
|
|
@@ -271,7 +305,7 @@ def wipe_users(ds):
|
|
|
271
305
|
|
|
272
306
|
def create_templates(ds: HowlerDatastore):
|
|
273
307
|
"""Create some random templates"""
|
|
274
|
-
for
|
|
308
|
+
for i in range(2):
|
|
275
309
|
keys = sample(list(Hit.flat_fields().keys()), 5)
|
|
276
310
|
|
|
277
311
|
for detection in ["Detection 1", "Detection 2"]:
|
|
@@ -284,6 +318,8 @@ def create_templates(ds: HowlerDatastore):
|
|
|
284
318
|
}
|
|
285
319
|
)
|
|
286
320
|
|
|
321
|
+
template = run_modifications("template", template, i == 0)
|
|
322
|
+
|
|
287
323
|
ds.template.save(
|
|
288
324
|
template.template_id,
|
|
289
325
|
template,
|
|
@@ -298,6 +334,8 @@ def create_templates(ds: HowlerDatastore):
|
|
|
298
334
|
}
|
|
299
335
|
)
|
|
300
336
|
|
|
337
|
+
template = run_modifications("template", template)
|
|
338
|
+
|
|
301
339
|
ds.template.save(
|
|
302
340
|
template.template_id,
|
|
303
341
|
template,
|
|
@@ -312,6 +350,8 @@ def create_templates(ds: HowlerDatastore):
|
|
|
312
350
|
}
|
|
313
351
|
)
|
|
314
352
|
|
|
353
|
+
template = run_modifications("template", template)
|
|
354
|
+
|
|
315
355
|
ds.template.save(
|
|
316
356
|
template.template_id,
|
|
317
357
|
template,
|
|
@@ -341,7 +381,7 @@ def wipe_templates(ds):
|
|
|
341
381
|
|
|
342
382
|
def create_overviews(ds: HowlerDatastore):
|
|
343
383
|
"""Create some random overviews"""
|
|
344
|
-
for
|
|
384
|
+
for i in range(2):
|
|
345
385
|
keys = sample(list(Hit.flat_fields().keys()), 5)
|
|
346
386
|
|
|
347
387
|
for detection in ["Detection 1", "Detection 2"]:
|
|
@@ -355,6 +395,8 @@ def create_overviews(ds: HowlerDatastore):
|
|
|
355
395
|
}
|
|
356
396
|
)
|
|
357
397
|
|
|
398
|
+
overview = run_modifications("overview", overview, i == 0)
|
|
399
|
+
|
|
358
400
|
ds.overview.save(
|
|
359
401
|
overview.overview_id,
|
|
360
402
|
overview,
|
|
@@ -399,6 +441,8 @@ def create_overviews(ds: HowlerDatastore):
|
|
|
399
441
|
}
|
|
400
442
|
)
|
|
401
443
|
|
|
444
|
+
overview = run_modifications("overview", overview)
|
|
445
|
+
|
|
402
446
|
ds.overview.save(
|
|
403
447
|
overview.overview_id,
|
|
404
448
|
overview,
|
|
@@ -423,6 +467,8 @@ def create_views(ds: HowlerDatastore):
|
|
|
423
467
|
}
|
|
424
468
|
)
|
|
425
469
|
|
|
470
|
+
view = run_modifications("view", view)
|
|
471
|
+
|
|
426
472
|
ds.view.save(
|
|
427
473
|
view.view_id,
|
|
428
474
|
view,
|
|
@@ -437,6 +483,8 @@ def create_views(ds: HowlerDatastore):
|
|
|
437
483
|
}
|
|
438
484
|
)
|
|
439
485
|
|
|
486
|
+
view = run_modifications("view", view)
|
|
487
|
+
|
|
440
488
|
ds.view.save(
|
|
441
489
|
view.view_id,
|
|
442
490
|
view,
|
|
@@ -455,6 +503,8 @@ def create_views(ds: HowlerDatastore):
|
|
|
455
503
|
}
|
|
456
504
|
)
|
|
457
505
|
|
|
506
|
+
view = run_modifications("view", view)
|
|
507
|
+
|
|
458
508
|
ds.view.save(
|
|
459
509
|
view.view_id,
|
|
460
510
|
view,
|
|
@@ -598,6 +648,8 @@ def create_analytics(ds: HowlerDatastore, num_analytics: int = 10):
|
|
|
598
648
|
)
|
|
599
649
|
)
|
|
600
650
|
|
|
651
|
+
analytic = run_modifications("analytic", analytic)
|
|
652
|
+
|
|
601
653
|
ds.analytic.save(analytic.analytic_id, analytic)
|
|
602
654
|
|
|
603
655
|
fields = Hit.flat_fields()
|
|
@@ -618,6 +670,8 @@ def create_analytics(ds: HowlerDatastore, num_analytics: int = 10):
|
|
|
618
670
|
set(random.sample(assessments, counts=([3] * len(assessments)), k=random.randint(1, len(assessments) * 3)))
|
|
619
671
|
)
|
|
620
672
|
|
|
673
|
+
a = run_modifications("analytic", a)
|
|
674
|
+
|
|
621
675
|
ds.analytic.save(a.analytic_id, a)
|
|
622
676
|
|
|
623
677
|
for rule_type in ["lucene", "eql", "sigma"]:
|
|
@@ -670,6 +724,8 @@ def create_analytics(ds: HowlerDatastore, num_analytics: int = 10):
|
|
|
670
724
|
"For better test data using sigma rules, execute howler/external/generate_sigma_rules.py."
|
|
671
725
|
)
|
|
672
726
|
|
|
727
|
+
a = run_modifications("analytic", a)
|
|
728
|
+
|
|
673
729
|
ds.analytic.save(a.analytic_id, a)
|
|
674
730
|
|
|
675
731
|
ds.analytic.commit()
|
|
@@ -735,6 +791,8 @@ def create_actions(ds: HowlerDatastore, num_actions: int = 30):
|
|
|
735
791
|
}
|
|
736
792
|
)
|
|
737
793
|
|
|
794
|
+
action = run_modifications("action", action)
|
|
795
|
+
|
|
738
796
|
ds.action.save(action.action_id, action)
|
|
739
797
|
|
|
740
798
|
ds.action.commit()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from howler.common.logging import get_logger
|
|
5
|
+
from howler.config import config as _config # Python gets BIG mad if we don't alias this
|
|
6
|
+
from howler.plugins.config import BasePluginConfig
|
|
7
|
+
|
|
8
|
+
logger = get_logger(__file__)
|
|
9
|
+
|
|
10
|
+
PLUGINS: dict[str, Optional[BasePluginConfig]] = {}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_plugins() -> list[BasePluginConfig]:
|
|
14
|
+
"Get a set of plugin configurations based on the howler settings."
|
|
15
|
+
for plugin in _config.core.plugins:
|
|
16
|
+
if plugin in PLUGINS:
|
|
17
|
+
continue
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
PLUGINS[plugin] = importlib.import_module(f"{plugin}.config").config
|
|
21
|
+
except (ImportError, ModuleNotFoundError):
|
|
22
|
+
logger.exception("Exception when loading plugin %s", plugin)
|
|
23
|
+
PLUGINS[plugin] = None
|
|
24
|
+
|
|
25
|
+
return [plugin for plugin in PLUGINS.values() if plugin]
|