howler-api 3.2.0.dev467__tar.gz → 3.2.0.dev470__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-3.2.0.dev467 → howler_api-3.2.0.dev470}/PKG-INFO +15 -17
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/__init__.py +2 -2
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/transition.py +1 -1
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/classification.py +3 -3
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/logging/__init__.py +2 -2
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/collection.py +4 -4
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/store.py +1 -1
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/generate_sigma_rules.py +1 -1
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/oauth.py +3 -3
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/workflow.py +4 -4
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/ws.py +1 -1
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/base.py +7 -9
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/config.py +1 -2
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/client.py +3 -4
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/code_signature.py +1 -3
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/email.py +7 -8
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/file.py +1 -2
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/geo.py +1 -3
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/rule.py +1 -2
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/url.py +2 -4
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/howler_data.py +2 -3
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/randomizer.py +1 -1
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/priority.py +1 -1
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/analytic_service.py +1 -1
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/user_service.py +1 -1
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/pyproject.toml +23 -26
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/README.md +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/add_label.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/add_to_bundle.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/change_field.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/demote.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/example_plugin.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/prioritization.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/promote.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/remove_from_bundle.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/remove_label.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/base.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/socket.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/action.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/analytic.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/auth.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/clue.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/configs.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/dossier.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/help.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/hit.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/notebook.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/overview.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/search.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/template.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/tool.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/user.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/utils/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/utils/etag.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/view.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/app.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/README.md +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/classification.yml +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/exceptions.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/loader.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/logging/audit.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/logging/format.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/net.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/net_static.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/random_user.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/swagger.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/config.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/cronjobs/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/cronjobs/retention.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/cronjobs/rules.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/cronjobs/view_cleanup.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/README.md +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/bulk.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/constants.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/exceptions.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/howler_store.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/migrations/fix_process.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/operations.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/schemas.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/support/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/support/build.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/support/schemas.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/types.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/error.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/generate_mitre.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/generate_tlds.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/reindex_data.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/wipe_databases.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/gunicorn_config.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/healthz.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/azure.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/discover.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/hit.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/search.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/README.md +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/charter.txt +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/helper.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/howler_enum.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/action.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/analytic.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/assemblyline.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/aws.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/azure.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/cbs.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/clue.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/dossier.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/agent.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/autonomous_system.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/cloud.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/container.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/dns.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/egress.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/elf.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/error.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/event.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/faas.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/group.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/hash.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/host.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/http.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/ingress.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/interface.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/network.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/observer.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/organization.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/os.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/pe.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/process.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/registry.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/related.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/server.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/threat.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/tls.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/user.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/user_agent.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/vulnerability.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/gcp.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/hit.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/lead.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/localized_label.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/overview.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/pivot.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/template.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/user.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/view.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/random_data.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/patched.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/plugins/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/plugins/config.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/README.md +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/counters.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/events.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/hash.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/lock.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/comms.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/multi.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/named.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/set.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/user_quota_tracker.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/security/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/security/socket.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/security/utils.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/action_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/auth_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/config_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/dossier_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/event_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/hit_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/jwt_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/lucene_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/notebook_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/overview_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/template_service.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/__init__.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/annotations.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/chunk.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/dict_utils.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/isotime.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/list_utils.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/lucene.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/path.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/socket_utils.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/str_utils.py +0 -0
- {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/uid.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: howler-api
|
|
3
|
-
Version: 3.2.0.
|
|
3
|
+
Version: 3.2.0.dev470
|
|
4
4
|
Summary: Howler - API server
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
|
|
@@ -19,40 +19,38 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.14
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
-
Requires-Dist: apscheduler (==3.
|
|
22
|
+
Requires-Dist: apscheduler (==3.11.2)
|
|
23
23
|
Requires-Dist: authlib (>=1.6.0,<2.0.0)
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist: chardet (==5.1.0)
|
|
24
|
+
Requires-Dist: bcrypt (==4.3.0)
|
|
25
|
+
Requires-Dist: chardet (==5.2.0)
|
|
27
26
|
Requires-Dist: chevron (==0.14.0)
|
|
28
27
|
Requires-Dist: elastic-apm[flask] (>=6.22.0,<7.0.0)
|
|
29
|
-
Requires-Dist: elasticsearch (==8.
|
|
28
|
+
Requires-Dist: elasticsearch (==8.19.3)
|
|
30
29
|
Requires-Dist: flasgger (>=0.9.7.1,<0.10.0.0)
|
|
31
|
-
Requires-Dist: flask (==2.
|
|
32
|
-
Requires-Dist: flask-caching (==2.
|
|
30
|
+
Requires-Dist: flask (==2.3.3)
|
|
31
|
+
Requires-Dist: flask-caching (==2.3.1)
|
|
33
32
|
Requires-Dist: gevent (==23.9.1)
|
|
34
33
|
Requires-Dist: gunicorn (==23.0.0)
|
|
35
34
|
Requires-Dist: luqum (>=1.0.0,<2.0.0)
|
|
36
35
|
Requires-Dist: mergedeep (>=1.3.4,<2.0.0)
|
|
37
36
|
Requires-Dist: packaging (<25.0)
|
|
38
37
|
Requires-Dist: passlib (==1.7.4)
|
|
39
|
-
Requires-Dist: prometheus-client (==0.
|
|
38
|
+
Requires-Dist: prometheus-client (==0.24.1)
|
|
40
39
|
Requires-Dist: pydantic (>=2.11.4,<3.0.0)
|
|
41
40
|
Requires-Dist: pydantic-settings[yaml] (>=2.9.1,<3.0.0)
|
|
42
41
|
Requires-Dist: pydash (>=8.0.5,<9.0.0)
|
|
43
|
-
Requires-Dist: pyjwt (==2.
|
|
44
|
-
Requires-Dist:
|
|
45
|
-
Requires-Dist: pysftp (==0.2.9)
|
|
46
|
-
Requires-Dist: pysigma (==0.11.17)
|
|
42
|
+
Requires-Dist: pyjwt (==2.11.0)
|
|
43
|
+
Requires-Dist: pysigma (==0.11.23)
|
|
47
44
|
Requires-Dist: pysigma-backend-elasticsearch (>=1.1.2,<2.0.0)
|
|
48
45
|
Requires-Dist: python-baseconv (==1.2.2)
|
|
49
46
|
Requires-Dist: python-datemath (==3.0.3)
|
|
50
47
|
Requires-Dist: python-dotenv (>=1.1.0,<2.0.0)
|
|
51
|
-
Requires-Dist:
|
|
52
|
-
Requires-Dist:
|
|
53
|
-
Requires-Dist:
|
|
48
|
+
Requires-Dist: pytz (>=2025.2,<2026.0)
|
|
49
|
+
Requires-Dist: pyyaml (==6.0.3)
|
|
50
|
+
Requires-Dist: redis (==4.6.0)
|
|
51
|
+
Requires-Dist: requests (==2.32.5)
|
|
54
52
|
Requires-Dist: typing-extensions (>=4.12.2,<5.0.0)
|
|
55
|
-
Requires-Dist: validators (>=0.34
|
|
53
|
+
Requires-Dist: validators (>=0.34,<0.36)
|
|
56
54
|
Requires-Dist: wsproto (==1.2.0)
|
|
57
55
|
Project-URL: Documentation, https://cybercentrecanada.github.io/howler/developer/backend/
|
|
58
56
|
Project-URL: Homepage, https://cybercentrecanada.github.io/howler/
|
|
@@ -51,9 +51,9 @@ def __sanitize_report(report: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
|
51
51
|
key = f"{entry['title']}==={entry['message']}==={entry['outcome']}"
|
|
52
52
|
|
|
53
53
|
if key in by_message:
|
|
54
|
-
by_message[key].append(f
|
|
54
|
+
by_message[key].append(f"({entry['query']})")
|
|
55
55
|
else:
|
|
56
|
-
by_message[key] = [f
|
|
56
|
+
by_message[key] = [f"({entry['query']})"]
|
|
57
57
|
|
|
58
58
|
sanitized: list[dict[str, Any]] = []
|
|
59
59
|
for key, queries in by_message.items():
|
|
@@ -41,7 +41,7 @@ def __parse_workflow_actions(workflow: Workflow) -> dict[str, set[str]]:
|
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
for key in wf_args:
|
|
44
|
-
entry = f
|
|
44
|
+
entry = f"transition:{str(wf['transition'])}"
|
|
45
45
|
|
|
46
46
|
if key in parsed_args:
|
|
47
47
|
parsed_args[key].add(entry)
|
|
@@ -103,7 +103,7 @@ class Classification(object):
|
|
|
103
103
|
self.NULL_CLASSIFICATION,
|
|
104
104
|
]:
|
|
105
105
|
raise InvalidDefinition(
|
|
106
|
-
"You cannot use reserved words NULL, INVALID or INV in your
|
|
106
|
+
"You cannot use reserved words NULL, INVALID or INV in your classification definition."
|
|
107
107
|
)
|
|
108
108
|
|
|
109
109
|
lvl = int(x["lvl"])
|
|
@@ -251,14 +251,14 @@ class Classification(object):
|
|
|
251
251
|
return self.levels_map[self.levels_aliases[lvl]]
|
|
252
252
|
else:
|
|
253
253
|
raise InvalidClassification(
|
|
254
|
-
"Classification level '%s' was not found in
|
|
254
|
+
"Classification level '%s' was not found in your classification definition." % lvl
|
|
255
255
|
)
|
|
256
256
|
|
|
257
257
|
def _get_c12n_level_text(self, lvl_idx: int, long_format: bool = True) -> str:
|
|
258
258
|
text = self.levels_map.get(str(lvl_idx), None)
|
|
259
259
|
if not text:
|
|
260
260
|
raise InvalidClassification(
|
|
261
|
-
"Classification level number '%s' was not
|
|
261
|
+
"Classification level number '%s' was not found in your classification definition." % lvl_idx
|
|
262
262
|
)
|
|
263
263
|
if long_format:
|
|
264
264
|
return self.levels_map_stl[text]
|
|
@@ -228,8 +228,8 @@ def log_with_traceback(traceback, msg, is_exception=False, audit=False):
|
|
|
228
228
|
|
|
229
229
|
try:
|
|
230
230
|
message = (
|
|
231
|
-
f
|
|
232
|
-
f
|
|
231
|
+
f"{tb_user['uname']} [{tb_user['classification']}] :: {msg} - {tb_file}:{tb_function}:{tb_line_no}"
|
|
232
|
+
f"[{os.environ.get('HOWLER_VERSION', '0.0.0.dev0')}] ({request.path}{args})"
|
|
233
233
|
)
|
|
234
234
|
if is_exception:
|
|
235
235
|
log.exception(message)
|
|
@@ -399,14 +399,14 @@ class ESCollection(Generic[ModelType]):
|
|
|
399
399
|
retries += 1
|
|
400
400
|
elif err_code == 429 or err_code == "429":
|
|
401
401
|
logger.warning(
|
|
402
|
-
"Elasticsearch is too busy to perform the requested
|
|
402
|
+
f"Elasticsearch is too busy to perform the requested task on index {self.name}, retrying..."
|
|
403
403
|
)
|
|
404
404
|
time.sleep(min(retries, self.MAX_RETRY_BACKOFF))
|
|
405
405
|
self.datastore.connection_reset()
|
|
406
406
|
retries += 1
|
|
407
407
|
elif err_code == 403 or err_code == "403":
|
|
408
408
|
logger.warning(
|
|
409
|
-
"Elasticsearch cluster is preventing writing operations
|
|
409
|
+
f"Elasticsearch cluster is preventing writing operations on index {self.name}, retrying..."
|
|
410
410
|
)
|
|
411
411
|
time.sleep(min(retries, self.MAX_RETRY_BACKOFF))
|
|
412
412
|
self.datastore.connection_reset()
|
|
@@ -842,7 +842,7 @@ class ESCollection(Generic[ModelType]):
|
|
|
842
842
|
key_list.remove(row["_id"])
|
|
843
843
|
add_to_output(row["_source"], row["_id"])
|
|
844
844
|
except ValueError:
|
|
845
|
-
logger.exception(f
|
|
845
|
+
logger.exception(f"MGet returned multiple documents for id: {row['_id']}")
|
|
846
846
|
|
|
847
847
|
if key_list and error_on_missing:
|
|
848
848
|
raise MultiKeyError(key_list, out)
|
|
@@ -2409,7 +2409,7 @@ class ESCollection(Generic[ModelType]):
|
|
|
2409
2409
|
# simply raise an exception
|
|
2410
2410
|
if no_fix:
|
|
2411
2411
|
raise HowlerValueError(
|
|
2412
|
-
f"Can't update database mapping for {self.name},
|
|
2412
|
+
f"Can't update database mapping for {self.name}, couldn't safely amend mapping for {no_fix}"
|
|
2413
2413
|
)
|
|
2414
2414
|
|
|
2415
2415
|
# If we got this far, the missing fields have been described in properties, upload them to the
|
|
@@ -247,7 +247,7 @@ class ESStore(object):
|
|
|
247
247
|
name_match = re.match(r"[a-z0-9_]*", name)
|
|
248
248
|
if not name_match or name_match.string != name:
|
|
249
249
|
raise DataStoreException(
|
|
250
|
-
"Invalid characters in model name.
|
|
250
|
+
"Invalid characters in model name. You can only use lower case letters, numbers and underscores."
|
|
251
251
|
)
|
|
252
252
|
|
|
253
253
|
self._models[name] = model_class
|
|
@@ -18,7 +18,7 @@ def main():
|
|
|
18
18
|
|
|
19
19
|
print("Copying files")
|
|
20
20
|
for network_yaml in (git_dir / "rules" / "network").glob("**/*.yml"):
|
|
21
|
-
print(f" {network_yaml.relative_to((
|
|
21
|
+
print(f" {network_yaml.relative_to((git_dir / 'rules' / 'network'))}")
|
|
22
22
|
new_file = output_dir / network_yaml.name
|
|
23
23
|
shutil.copyfile(network_yaml, new_file)
|
|
24
24
|
|
|
@@ -183,7 +183,7 @@ def fetch_avatar( # noqa: C901
|
|
|
183
183
|
|
|
184
184
|
if resp.ok and resp.headers.get("content-type") is not None:
|
|
185
185
|
b64_img = base64.b64encode(resp.content).decode()
|
|
186
|
-
avatar = f
|
|
186
|
+
avatar = f"data:{resp.headers.get('content-type')};base64,{b64_img}"
|
|
187
187
|
return avatar
|
|
188
188
|
|
|
189
189
|
# Url that is protected through OAuth
|
|
@@ -191,7 +191,7 @@ def fetch_avatar( # noqa: C901
|
|
|
191
191
|
resp = provider.get(url[len(provider_config.api_base_url) :])
|
|
192
192
|
if resp.ok and resp.headers.get("content-type") is not None:
|
|
193
193
|
b64_img = base64.b64encode(resp.content).decode()
|
|
194
|
-
avatar = f
|
|
194
|
+
avatar = f"data:{resp.headers.get('content-type')};base64,{b64_img}"
|
|
195
195
|
return avatar
|
|
196
196
|
|
|
197
197
|
# Unprotected url
|
|
@@ -199,7 +199,7 @@ def fetch_avatar( # noqa: C901
|
|
|
199
199
|
resp = requests.get(url, timeout=10)
|
|
200
200
|
if resp.ok and resp.headers.get("content-type") is not None:
|
|
201
201
|
b64_img = base64.b64encode(resp.content).decode()
|
|
202
|
-
avatar = f
|
|
202
|
+
avatar = f"data:{resp.headers.get('content-type')};base64,{b64_img}"
|
|
203
203
|
return avatar
|
|
204
204
|
|
|
205
205
|
# Quietly fail, it'll use gravatar instead
|
|
@@ -51,11 +51,11 @@ class Workflow:
|
|
|
51
51
|
for t in transitions:
|
|
52
52
|
if t.get("source", False) and isinstance(t["source"], list):
|
|
53
53
|
for s in t["source"]:
|
|
54
|
-
self.transitions[f
|
|
55
|
-
identifiers.append(f
|
|
54
|
+
self.transitions[f"{s}{t['transition']}"] = t
|
|
55
|
+
identifiers.append(f"{s}{t['transition']}{t.get('dest', None) or ''}")
|
|
56
56
|
else:
|
|
57
|
-
self.transitions[f
|
|
58
|
-
identifiers.append(f
|
|
57
|
+
self.transitions[f"{t.get('source', '') or ''}{t['transition']}"] = t
|
|
58
|
+
identifiers.append(f"{t.get('source', '') or ''}{t['transition']}{t.get('dest', '') or ''}")
|
|
59
59
|
|
|
60
60
|
if len(set(identifiers)) != len(identifiers):
|
|
61
61
|
raise WorkflowException("There are duplicate transitions (same source, transition and dest values).")
|
|
@@ -33,7 +33,7 @@ class ConnectionClosed(RuntimeError):
|
|
|
33
33
|
def __init__(self, reason=CloseReason.NO_STATUS_RCVD, message=None):
|
|
34
34
|
self.reason = reason
|
|
35
35
|
self.message = message
|
|
36
|
-
super().__init__(f
|
|
36
|
+
super().__init__(f"Connection closed: {reason} {message or ''}")
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class Base:
|
|
@@ -202,7 +202,7 @@ class _Field:
|
|
|
202
202
|
|
|
203
203
|
def check(self, value, **kwargs):
|
|
204
204
|
raise HowlerNotImplementedError(
|
|
205
|
-
"This function is not defined in the default field.
|
|
205
|
+
"This function is not defined in the default field. Each fields has to have their own definition"
|
|
206
206
|
)
|
|
207
207
|
|
|
208
208
|
def __repr__(self) -> str:
|
|
@@ -469,7 +469,7 @@ class Email(Keyword):
|
|
|
469
469
|
match = self.validation_regex.match(value)
|
|
470
470
|
if not is_valid_domain(match.group(1)):
|
|
471
471
|
raise HowlerValueError(
|
|
472
|
-
f"[{'.'.join(context) or self.name}] '{match.group(1)}' in email '{value}'
|
|
472
|
+
f"[{'.'.join(context) or self.name}] '{match.group(1)}' in email '{value}' is not a valid Domain."
|
|
473
473
|
)
|
|
474
474
|
|
|
475
475
|
return value.lower()
|
|
@@ -487,14 +487,12 @@ class URI(Keyword):
|
|
|
487
487
|
match = self.validation_regex.match(value)
|
|
488
488
|
if not match:
|
|
489
489
|
raise HowlerValueError(
|
|
490
|
-
f"[{'.'.join(context) or self.name}] '{value}' not match the "
|
|
491
|
-
f"validator: {self.validation_regex.pattern}"
|
|
490
|
+
f"[{'.'.join(context) or self.name}] '{value}' not match the validator: {self.validation_regex.pattern}"
|
|
492
491
|
)
|
|
493
492
|
|
|
494
493
|
if not is_valid_domain(match.group(2)) and not is_valid_ip(match.group(2)):
|
|
495
494
|
raise HowlerValueError(
|
|
496
|
-
f"[{'.'.join(context) or self.name}] '{match.group(2)}' in URI '{value}'"
|
|
497
|
-
" is not a valid Domain or IP."
|
|
495
|
+
f"[{'.'.join(context) or self.name}] '{match.group(2)}' in URI '{value}' is not a valid Domain or IP."
|
|
498
496
|
)
|
|
499
497
|
|
|
500
498
|
return match.group(0).replace(match.group(1), match.group(1).lower())
|
|
@@ -1199,7 +1197,7 @@ class Model:
|
|
|
1199
1197
|
)
|
|
1200
1198
|
|
|
1201
1199
|
# Header
|
|
1202
|
-
markdown_content += f"{'#'*toc_depth} {cls.__name__}\n\n> {cls.__description}\n\n"
|
|
1200
|
+
markdown_content += f"{'#' * toc_depth} {cls.__name__}\n\n> {cls.__description}\n\n"
|
|
1203
1201
|
|
|
1204
1202
|
# Table
|
|
1205
1203
|
table = "| Field | Type | Description | Required | Default |\n| :--- | :--- | :--- | :--- | :--- |\n"
|
|
@@ -1251,7 +1249,7 @@ class Model:
|
|
|
1251
1249
|
|
|
1252
1250
|
values = [f'"{v}"' if v else str(v) for v in sorted(values)]
|
|
1253
1251
|
values.append("None") if none_value else None
|
|
1254
|
-
description = f
|
|
1252
|
+
description = f"{description}<br>Values:<br>`{', '.join(values)}`"
|
|
1255
1253
|
|
|
1256
1254
|
# Is this a required field?
|
|
1257
1255
|
if info.__class__ != Optional and not info.optional:
|
|
@@ -1340,7 +1338,7 @@ class Model:
|
|
|
1340
1338
|
extra_keys = set(extra_fields.keys()) - set(data.keys())
|
|
1341
1339
|
if self.unused_keys and not ignore_extra_values:
|
|
1342
1340
|
raise HowlerValueError(
|
|
1343
|
-
f"[{'.'.join(context)}]: object was created with invalid parameters:
|
|
1341
|
+
f"[{'.'.join(context)}]: object was created with invalid parameters: {', '.join(self.unused_keys)}"
|
|
1344
1342
|
)
|
|
1345
1343
|
|
|
1346
1344
|
# Pass each value through it's respective validator, and store it
|
|
@@ -327,8 +327,7 @@ class Retention(BaseModel):
|
|
|
327
327
|
enabled: bool = Field(
|
|
328
328
|
default=True,
|
|
329
329
|
description=(
|
|
330
|
-
"Whether to enable the hit retention limit. If enabled, hits will "
|
|
331
|
-
"be purged after the specified duration."
|
|
330
|
+
"Whether to enable the hit retention limit. If enabled, hits will be purged after the specified duration."
|
|
332
331
|
),
|
|
333
332
|
)
|
|
334
333
|
limit_unit: Literal["days", "seconds", "microseconds", "milliseconds", "minutes", "hours", "weeks"] = Field(
|
|
@@ -45,15 +45,14 @@ class OriginalClient(odm.Model):
|
|
|
45
45
|
bytes: Optional[int] = odm.Optional(
|
|
46
46
|
odm.Integer(
|
|
47
47
|
description=(
|
|
48
|
-
"The original client in a session that has changed clients. "
|
|
49
|
-
"Bytes sent from the client to the server."
|
|
48
|
+
"The original client in a session that has changed clients. Bytes sent from the client to the server."
|
|
50
49
|
)
|
|
51
50
|
)
|
|
52
51
|
)
|
|
53
52
|
domain: Optional[str] = odm.Optional(
|
|
54
53
|
odm.Domain(
|
|
55
54
|
description=(
|
|
56
|
-
"The original client in a session that has changed clients.
|
|
55
|
+
"The original client in a session that has changed clients. The domain name of the client system."
|
|
57
56
|
)
|
|
58
57
|
)
|
|
59
58
|
)
|
|
@@ -69,7 +68,7 @@ class OriginalClient(odm.Model):
|
|
|
69
68
|
ip: Optional[str] = odm.Optional(
|
|
70
69
|
odm.IP(
|
|
71
70
|
description=(
|
|
72
|
-
"The original client in a session that has changed clients. IP address of the
|
|
71
|
+
"The original client in a session that has changed clients. IP address of the client (IPv4 or IPv6)."
|
|
73
72
|
)
|
|
74
73
|
)
|
|
75
74
|
)
|
|
@@ -21,7 +21,5 @@ class CodeSignature(odm.Model):
|
|
|
21
21
|
timestamp = odm.Optional(odm.Date(description="Date and time when the code signature was generated and signed."))
|
|
22
22
|
trusted = odm.Optional(odm.Boolean(description="Stores the trust status of the certificate chain."))
|
|
23
23
|
valid = odm.Optional(
|
|
24
|
-
odm.Boolean(
|
|
25
|
-
description="Boolean to capture if the digital signature" " is verified against the binary content."
|
|
26
|
-
)
|
|
24
|
+
odm.Boolean(description="Boolean to capture if the digital signature is verified against the binary content.")
|
|
27
25
|
)
|
|
@@ -36,7 +36,7 @@ class ParentEmail(odm.Model):
|
|
|
36
36
|
from_ = odm.Optional(
|
|
37
37
|
odm.Compound(
|
|
38
38
|
Address,
|
|
39
|
-
description="The email address of the sender, typically
|
|
39
|
+
description="The email address of the sender, typically from the RFC 5322 From: header field.",
|
|
40
40
|
)
|
|
41
41
|
)
|
|
42
42
|
message_id = odm.Optional(
|
|
@@ -70,19 +70,19 @@ class Email(odm.Model):
|
|
|
70
70
|
cc = odm.Optional(odm.Compound(Address, description="The email address of CC recipient."))
|
|
71
71
|
content_type = odm.Optional(odm.Keyword(description="Information about how the message is to be displayed."))
|
|
72
72
|
delivery_timestamp = odm.Optional(
|
|
73
|
-
odm.Date(description="The date and time when the email message
|
|
73
|
+
odm.Date(description="The date and time when the email message was received by the service or client.")
|
|
74
74
|
)
|
|
75
75
|
direction = odm.Optional(
|
|
76
|
-
odm.Keyword(description="The direction of the message based on the
|
|
76
|
+
odm.Keyword(description="The direction of the message based on the sending and receiving domains.")
|
|
77
77
|
)
|
|
78
78
|
from_ = odm.Optional(
|
|
79
79
|
odm.Compound(
|
|
80
80
|
Address,
|
|
81
|
-
description="The email address of the sender, typically
|
|
81
|
+
description="The email address of the sender, typically from the RFC 5322 From: header field.",
|
|
82
82
|
)
|
|
83
83
|
)
|
|
84
84
|
local_id = odm.Optional(
|
|
85
|
-
odm.Keyword(description="Unique identifier given to the email by the source
|
|
85
|
+
odm.Keyword(description="Unique identifier given to the email by the source that created the event.")
|
|
86
86
|
)
|
|
87
87
|
message_id = odm.Optional(
|
|
88
88
|
odm.Keyword(
|
|
@@ -101,15 +101,14 @@ class Email(odm.Model):
|
|
|
101
101
|
sender = odm.Optional(
|
|
102
102
|
odm.Compound(
|
|
103
103
|
Address,
|
|
104
|
-
description="Per RFC 5322, specifies the address responsible for "
|
|
105
|
-
"the actual transmission of the message.",
|
|
104
|
+
description="Per RFC 5322, specifies the address responsible for the actual transmission of the message.",
|
|
106
105
|
)
|
|
107
106
|
)
|
|
108
107
|
subject = odm.Optional(odm.Keyword(description="A brief summary of the topic of the message."))
|
|
109
108
|
to = odm.Optional(odm.Compound(Address, description="The email address of recipient."))
|
|
110
109
|
x_mailer = odm.Optional(
|
|
111
110
|
odm.Keyword(
|
|
112
|
-
description="The name of the application that was used to draft
|
|
111
|
+
description="The name of the application that was used to draft and send the original email message."
|
|
113
112
|
)
|
|
114
113
|
)
|
|
115
114
|
|
|
@@ -14,8 +14,7 @@ FILE_TYPE = ["file", "dir", "symlink"]
|
|
|
14
14
|
@odm.model(
|
|
15
15
|
index=True,
|
|
16
16
|
store=True,
|
|
17
|
-
description="A file is defined as a set of information that has "
|
|
18
|
-
"been created on, or has existed on a filesystem.",
|
|
17
|
+
description="A file is defined as a set of information that has been created on, or has existed on a filesystem.",
|
|
19
18
|
)
|
|
20
19
|
class File(odm.Model):
|
|
21
20
|
accessed: Optional[str] = odm.Optional(odm.Date(description="Last time the file was accessed."))
|
|
@@ -20,9 +20,7 @@ class Geo(odm.Model):
|
|
|
20
20
|
country_name = odm.Optional(odm.Keyword(description="Country name."))
|
|
21
21
|
location = odm.Optional(odm.Compound(GeoPoint, description="Longitude and latitude."))
|
|
22
22
|
name = odm.Optional(
|
|
23
|
-
odm.Keyword(
|
|
24
|
-
description="User-defined description of a location, at the level " "of granularity they care about."
|
|
25
|
-
)
|
|
23
|
+
odm.Keyword(description="User-defined description of a location, at the level of granularity they care about.")
|
|
26
24
|
)
|
|
27
25
|
postal_code = odm.Optional(odm.Keyword(description="Postal code associated with the location."))
|
|
28
26
|
region_iso_code = odm.Optional(odm.Keyword(description="Region ISO code."))
|
|
@@ -16,8 +16,7 @@ class Rule(odm.Model):
|
|
|
16
16
|
)
|
|
17
17
|
category = odm.Optional(
|
|
18
18
|
odm.Keyword(
|
|
19
|
-
description="A categorization value keyword used by the entity using the "
|
|
20
|
-
"rule for detection of this event."
|
|
19
|
+
description="A categorization value keyword used by the entity using the rule for detection of this event."
|
|
21
20
|
)
|
|
22
21
|
)
|
|
23
22
|
description = odm.Optional(odm.Keyword(description="The description of the rule generating the event."))
|
|
@@ -27,12 +27,10 @@ class URL(odm.Model):
|
|
|
27
27
|
path = odm.Optional(odm.Keyword(description='Path of the request, such as "/search".'))
|
|
28
28
|
port = odm.Optional(odm.Integer(description="Port of the request, such as 443."))
|
|
29
29
|
query = odm.Optional(
|
|
30
|
-
odm.Keyword(
|
|
31
|
-
description="The query field describes the query string of the " 'request, such as "q=elasticsearch".'
|
|
32
|
-
)
|
|
30
|
+
odm.Keyword(description='The query field describes the query string of the request, such as "q=elasticsearch".')
|
|
33
31
|
)
|
|
34
32
|
registered_domain = odm.Optional(
|
|
35
|
-
odm.Keyword(description="The highest registered url domain,
|
|
33
|
+
odm.Keyword(description="The highest registered url domain, stripped of the subdomain.")
|
|
36
34
|
)
|
|
37
35
|
scheme = odm.Optional(odm.Keyword(description='Scheme of the request, such as "https".'))
|
|
38
36
|
subdomain = odm.Optional(
|
|
@@ -116,8 +116,7 @@ class Link(odm.Model):
|
|
|
116
116
|
title = odm.Keyword(description="The title to use for the link.", optional=True)
|
|
117
117
|
icon = odm.Keyword(
|
|
118
118
|
description=(
|
|
119
|
-
"The icon to show. Either an ID corresponding to an "
|
|
120
|
-
"analytical platform application, or an external link."
|
|
119
|
+
"The icon to show. Either an ID corresponding to an analytical platform application, or an external link."
|
|
121
120
|
),
|
|
122
121
|
optional=True,
|
|
123
122
|
)
|
|
@@ -288,7 +287,7 @@ class HowlerData(odm.Model):
|
|
|
288
287
|
rationale: Optional[str] = odm.Optional(
|
|
289
288
|
odm.Keyword(
|
|
290
289
|
description=(
|
|
291
|
-
"The rationale behind the hit assessment. Allows it to be understood and
|
|
290
|
+
"The rationale behind the hit assessment. Allows it to be understood and verified by other analysts."
|
|
292
291
|
)
|
|
293
292
|
)
|
|
294
293
|
)
|
|
@@ -379,7 +379,7 @@ def get_random_mapping(field: _Field) -> dict[str, _Any]:
|
|
|
379
379
|
def get_random_phone() -> str:
|
|
380
380
|
"""Get a random phone"""
|
|
381
381
|
return (
|
|
382
|
-
f
|
|
382
|
+
f"{random.choice(['', '+1 '])}{'-'.join([str(random.randint(100, 999)) for _ in range(3)])}"
|
|
383
383
|
f"{str(random.randint(0, 9))}"
|
|
384
384
|
)
|
|
385
385
|
|
{howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/priority.py
RENAMED
|
@@ -99,7 +99,7 @@ class PriorityQueue(Generic[T]):
|
|
|
99
99
|
|
|
100
100
|
def push(self, priority: int, data: T, vip=None):
|
|
101
101
|
vip = 0 if vip else 9
|
|
102
|
-
value = f"{vip}{f'{int(time.time()*1000000):020}'}{json.dumps(data)}"
|
|
102
|
+
value = f"{vip}{f'{int(time.time() * 1000000):020}'}{json.dumps(data)}"
|
|
103
103
|
retry_call(self.c.zadd, self.name, {value: -priority})
|
|
104
104
|
return value
|
|
105
105
|
|
|
@@ -86,7 +86,7 @@ def get_matching_analytics(hits: Union[list[Hit], list[dict[str, Any]]]) -> list
|
|
|
86
86
|
|
|
87
87
|
try:
|
|
88
88
|
existing_analytics: list[Analytic] = storage.analytic.search(
|
|
89
|
-
f
|
|
89
|
+
f"name:({' OR '.join(analytic_names)})", as_obj=True
|
|
90
90
|
)["items"]
|
|
91
91
|
|
|
92
92
|
return existing_analytics
|
|
@@ -282,7 +282,7 @@ def add_access_control(user: dict[str, Any]):
|
|
|
282
282
|
if req_query:
|
|
283
283
|
req_query = f"-({req_query}) AND "
|
|
284
284
|
|
|
285
|
-
lvl_query = f
|
|
285
|
+
lvl_query = f"__access_lvl__:[0 TO {user['__access_lvl__']}]"
|
|
286
286
|
|
|
287
287
|
query = f"{gl2_query}{gl1_query}{req_query}{lvl_query}"
|
|
288
288
|
user["access_control"] = safe_str(query)
|
|
@@ -152,7 +152,7 @@ suppress-none-returning = true
|
|
|
152
152
|
[tool.poetry]
|
|
153
153
|
package-mode = true
|
|
154
154
|
name = "howler-api"
|
|
155
|
-
version = "3.2.0.
|
|
155
|
+
version = "3.2.0.dev470"
|
|
156
156
|
description = "Howler - API server"
|
|
157
157
|
authors = [
|
|
158
158
|
"Canadian Centre for Cyber Security <howler@cyber.gc.ca>",
|
|
@@ -196,45 +196,43 @@ repository = "https://github.com/CybercentreCanada/howler-api"
|
|
|
196
196
|
|
|
197
197
|
[tool.poetry.dependencies]
|
|
198
198
|
python = "^3.9.17"
|
|
199
|
-
apscheduler = "3.
|
|
199
|
+
apscheduler = "3.11.2"
|
|
200
200
|
authlib = "^1.6.0"
|
|
201
|
-
|
|
202
|
-
azure-storage-blob = "12.14.1"
|
|
203
|
-
chardet = "5.1.0"
|
|
201
|
+
chardet = "5.2.0"
|
|
204
202
|
elastic-apm = { extras = ["flask"], version = "^6.22.0" }
|
|
205
|
-
elasticsearch = "8.
|
|
206
|
-
flask = "2.
|
|
207
|
-
flask-caching = "2.
|
|
203
|
+
elasticsearch = "8.19.3"
|
|
204
|
+
flask = "2.3.3"
|
|
205
|
+
flask-caching = "2.3.1"
|
|
208
206
|
gevent = "23.9.1"
|
|
209
207
|
gunicorn = "23.0.0"
|
|
210
208
|
packaging = "<25.0"
|
|
211
209
|
passlib = "1.7.4"
|
|
212
|
-
prometheus-client = "0.
|
|
213
|
-
pyjwt = "2.
|
|
214
|
-
pyroute2-core = "0.6.13"
|
|
215
|
-
pysftp = "0.2.9"
|
|
210
|
+
prometheus-client = "0.24.1"
|
|
211
|
+
pyjwt = "2.11.0"
|
|
216
212
|
python-baseconv = "1.2.2"
|
|
217
213
|
python-datemath = "3.0.3"
|
|
218
|
-
pyyaml = "6.0.
|
|
219
|
-
redis = "4.
|
|
220
|
-
requests = "2.32.
|
|
214
|
+
pyyaml = "6.0.3"
|
|
215
|
+
redis = "4.6.0"
|
|
216
|
+
requests = "2.32.5"
|
|
221
217
|
wsproto = "1.2.0"
|
|
222
218
|
chevron = "0.14.0"
|
|
223
219
|
typing-extensions = "^4.12.2"
|
|
224
220
|
flasgger = "^0.9.7.1"
|
|
225
|
-
pysigma = "0.11.
|
|
221
|
+
pysigma = "0.11.23"
|
|
226
222
|
pysigma-backend-elasticsearch = "^1.1.2"
|
|
227
223
|
mergedeep = "^1.3.4"
|
|
228
|
-
validators = "
|
|
224
|
+
validators = ">=0.34,<0.36"
|
|
229
225
|
python-dotenv = "^1.1.0"
|
|
230
226
|
pydantic = "^2.11.4"
|
|
231
227
|
pydantic-settings = { extras = ["yaml"], version = "^2.9.1" }
|
|
232
228
|
luqum = "^1.0.0"
|
|
233
229
|
pydash = "^8.0.5"
|
|
230
|
+
pytz = "^2025.2"
|
|
231
|
+
bcrypt = "4.3.0"
|
|
234
232
|
|
|
235
233
|
[tool.poetry.group.dev.dependencies]
|
|
236
234
|
pre-commit = "^3.7.0"
|
|
237
|
-
ruff = "
|
|
235
|
+
ruff = ">=0.8,<0.16"
|
|
238
236
|
pyright = {extras = ["nodejs"], version = "^1.1.408"}
|
|
239
237
|
mypy = "^1.6.1"
|
|
240
238
|
|
|
@@ -250,17 +248,16 @@ mypy-extensions = "^1.0.0"
|
|
|
250
248
|
coverage = { extras = ["toml"], version = "^7.4.4" }
|
|
251
249
|
|
|
252
250
|
[tool.poetry.group.types.dependencies]
|
|
253
|
-
types-PyYAML = "6.0.12.
|
|
254
|
-
types-paramiko = "3.
|
|
255
|
-
types-pyOpenSSL = "23.3.0.
|
|
256
|
-
types-redis = "4.6.0.
|
|
257
|
-
types-six = "1.
|
|
258
|
-
types-requests = "<2.
|
|
259
|
-
websocket-client = "1.
|
|
251
|
+
types-PyYAML = "6.0.12.20250915"
|
|
252
|
+
types-paramiko = "3.5.0.20250801"
|
|
253
|
+
types-pyOpenSSL = "23.3.0.20240106"
|
|
254
|
+
types-redis = "4.6.0.20241004"
|
|
255
|
+
types-six = "1.17.0.20251009"
|
|
256
|
+
types-requests = "<2.32.4.20260108"
|
|
257
|
+
websocket-client = "1.9.0"
|
|
260
258
|
types-pytz = "^2024.1.0.20240417"
|
|
261
259
|
types-mock = "^5.2.0.20250306"
|
|
262
260
|
|
|
263
|
-
|
|
264
261
|
[tool.poetry.group.pre-commit.dependencies]
|
|
265
262
|
rich = "^14.2.0"
|
|
266
263
|
conventional-pre-commit = "^4.3.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|