howler-api 3.2.0.dev438__tar.gz → 3.2.0.dev467__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.dev438 → howler_api-3.2.0.dev467}/PKG-INFO +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/prioritization.py +2 -2
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/transition.py +2 -2
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/__init__.py +9 -2
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/socket.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/__init__.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/action.py +2 -2
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/analytic.py +15 -24
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/auth.py +11 -9
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/clue.py +3 -3
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/configs.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/dossier.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/help.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/hit.py +28 -27
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/notebook.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/overview.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/search.py +7 -8
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/template.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/tool.py +3 -3
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/user.py +11 -11
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/view.py +7 -5
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/classification.py +2 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/logging/__init__.py +2 -2
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/swagger.py +2 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/bulk.py +11 -11
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/collection.py +120 -18
- howler_api-3.2.0.dev467/howler/datastore/types.py +11 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/error.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/helper/hit.py +2 -2
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/helper/oauth.py +6 -4
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/base.py +3 -3
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/helper.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/howler_data.py +3 -3
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/user.py +11 -11
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/random_data.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/security/__init__.py +5 -4
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/security/socket.py +23 -19
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/security/utils.py +2 -2
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/analytic_service.py +33 -5
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/auth_service.py +6 -5
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/dossier_service.py +33 -5
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/hit_service.py +75 -16
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/jwt_service.py +5 -5
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/lucene_service.py +3 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/template_service.py +16 -4
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/user_service.py +26 -6
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/lucene.py +1 -1
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/pyproject.toml +8 -3
- howler_api-3.2.0.dev438/howler/datastore/types.py +0 -22
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/README.md +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/add_label.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/add_to_bundle.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/change_field.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/demote.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/example_plugin.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/promote.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/remove_from_bundle.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/actions/remove_label.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/base.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/utils/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/api/v1/utils/etag.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/app.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/README.md +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/classification.yml +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/exceptions.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/loader.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/logging/audit.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/logging/format.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/net.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/net_static.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/common/random_user.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/config.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/cronjobs/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/cronjobs/retention.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/cronjobs/rules.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/cronjobs/view_cleanup.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/README.md +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/constants.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/exceptions.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/howler_store.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/migrations/fix_process.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/operations.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/schemas.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/store.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/support/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/support/build.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/datastore/support/schemas.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/external/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/external/generate_mitre.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/external/generate_sigma_rules.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/external/generate_tlds.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/external/reindex_data.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/external/wipe_databases.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/gunicorn_config.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/healthz.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/helper/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/helper/azure.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/helper/discover.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/helper/search.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/helper/workflow.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/helper/ws.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/README.md +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/charter.txt +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/howler_enum.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/action.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/analytic.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/assemblyline.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/aws.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/azure.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/cbs.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/clue.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/config.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/dossier.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/agent.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/autonomous_system.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/client.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/cloud.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/code_signature.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/container.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/dns.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/egress.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/elf.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/email.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/error.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/event.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/faas.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/file.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/geo.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/group.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/hash.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/host.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/http.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/ingress.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/interface.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/network.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/observer.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/organization.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/os.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/pe.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/process.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/registry.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/related.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/rule.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/server.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/threat.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/tls.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/url.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/user.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/user_agent.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/ecs/vulnerability.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/gcp.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/hit.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/lead.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/localized_label.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/overview.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/pivot.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/template.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/models/view.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/odm/randomizer.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/patched.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/plugins/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/plugins/config.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/README.md +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/counters.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/events.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/hash.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/lock.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/queues/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/queues/comms.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/queues/multi.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/queues/named.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/queues/priority.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/set.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/remote/datatypes/user_quota_tracker.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/action_service.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/config_service.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/event_service.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/notebook_service.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/services/overview_service.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/__init__.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/annotations.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/chunk.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/dict_utils.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/isotime.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/list_utils.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/path.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/socket_utils.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/str_utils.py +0 -0
- {howler_api-3.2.0.dev438 → howler_api-3.2.0.dev467}/howler/utils/uid.py +0 -0
|
@@ -10,7 +10,7 @@ OPERATION_ID = "prioritization"
|
|
|
10
10
|
VALID_FIELDS = ["reliability", "severity", "volume", "confidence", "score"]
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def execute(query: str, field: str = "score", value: str = "0.0", **kwargs):
|
|
13
|
+
def execute(query: str, field: str = "score", value: str | float = "0.0", **kwargs):
|
|
14
14
|
"""Change one of the priorization fields of a hit
|
|
15
15
|
|
|
16
16
|
Args:
|
|
@@ -34,7 +34,7 @@ def execute(query: str, field: str = "score", value: str = "0.0", **kwargs):
|
|
|
34
34
|
report = []
|
|
35
35
|
|
|
36
36
|
try:
|
|
37
|
-
value
|
|
37
|
+
value = float(value)
|
|
38
38
|
|
|
39
39
|
datastore().hit.update_by_query(
|
|
40
40
|
query,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import inspect
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional, cast
|
|
3
3
|
|
|
4
4
|
from howler.common.exceptions import InvalidDataException, NotFoundException
|
|
5
5
|
from howler.common.loader import datastore
|
|
@@ -115,7 +115,7 @@ def execute(
|
|
|
115
115
|
try:
|
|
116
116
|
hit_service.transition_hit(
|
|
117
117
|
hit_id,
|
|
118
|
-
HitStatusTransition[transition],
|
|
118
|
+
cast(HitStatusTransition, HitStatusTransition[transition]),
|
|
119
119
|
user,
|
|
120
120
|
**kwargs,
|
|
121
121
|
)
|
|
@@ -28,8 +28,15 @@ def make_subapi_blueprint(name, api_version=1):
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def _make_api_response(
|
|
31
|
-
data: Any,
|
|
31
|
+
data: Any,
|
|
32
|
+
err: Union[str, Exception] = "",
|
|
33
|
+
warnings: list[str] | None = None,
|
|
34
|
+
status_code: int = 200,
|
|
35
|
+
cookies: Any = None,
|
|
32
36
|
) -> Response:
|
|
37
|
+
if not warnings:
|
|
38
|
+
warnings = []
|
|
39
|
+
|
|
33
40
|
quota_user = flsk_session.pop("quota_user", None)
|
|
34
41
|
quota_set = flsk_session.pop("quota_set", False)
|
|
35
42
|
if quota_user and quota_set and not request.path.startswith("/api/v1/clue"):
|
|
@@ -100,7 +107,7 @@ def not_modified(data=DEFAULT_DATA[True], cookies=None):
|
|
|
100
107
|
return _make_api_response(data, status_code=304, cookies=cookies)
|
|
101
108
|
|
|
102
109
|
|
|
103
|
-
def bad_request(data=DEFAULT_DATA[False], err="", cookies=None, warnings=None):
|
|
110
|
+
def bad_request(data: Any = DEFAULT_DATA[False], err: str = "", cookies: Any = None, warnings: list[str] | None = None):
|
|
104
111
|
"""Returns response with status code ies"""
|
|
105
112
|
return _make_api_response(data, err, status_code=400, cookies=cookies, warnings=warnings)
|
|
106
113
|
|
|
@@ -46,7 +46,7 @@ def emit(event: str):
|
|
|
46
46
|
return ok()
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
@socket_api.route("/connect", websocket=True)
|
|
49
|
+
@socket_api.route("/connect", websocket=True) # type: ignore
|
|
50
50
|
@websocket_auth(required_priv=["R"])
|
|
51
51
|
def connect(ws: Server, *args: Any, ws_id: str, **kwargs):
|
|
52
52
|
"""Connect to the server to monitor for updates via websocket
|
|
@@ -7,7 +7,7 @@ from howler.security import api_login
|
|
|
7
7
|
|
|
8
8
|
API_PREFIX = "/api/v1"
|
|
9
9
|
apiv1 = Blueprint("apiv1", __name__, url_prefix=API_PREFIX)
|
|
10
|
-
apiv1._doc = "Api Documentation Version 1" # type: ignore[attr-defined]
|
|
10
|
+
apiv1._doc = "Api Documentation Version 1" # type: ignore[attr-defined] # type: ignore
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@apiv1.route("/")
|
|
@@ -18,7 +18,7 @@ SUB_API = "action"
|
|
|
18
18
|
classification_definition = CLASSIFICATION.get_parsed_classification_definition()
|
|
19
19
|
|
|
20
20
|
action_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
21
|
-
action_api._doc = "Endpoints relating to bulk actions and automation"
|
|
21
|
+
action_api._doc = "Endpoints relating to bulk actions and automation" # type: ignore
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@generate_swagger_docs()
|
|
@@ -240,7 +240,7 @@ def execute_action(id: str, **kwargs) -> Response:
|
|
|
240
240
|
current_user: User | None = kwargs.get("user", None)
|
|
241
241
|
|
|
242
242
|
for operation in action.operations:
|
|
243
|
-
op_data = json.loads(operation
|
|
243
|
+
op_data = json.loads(operation.data_json) if operation.data_json else {}
|
|
244
244
|
|
|
245
245
|
query = execute_req.get("query", action.query) or action.query
|
|
246
246
|
|
|
@@ -27,7 +27,7 @@ from howler.services import analytic_service, user_service
|
|
|
27
27
|
MAX_COMMENT_LEN = 5000
|
|
28
28
|
SUB_API = "analytic"
|
|
29
29
|
analytic_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
30
|
-
analytic_api._doc = "Manage the analytics that create hits"
|
|
30
|
+
analytic_api._doc = "Manage the analytics that create hits" # type: ignore
|
|
31
31
|
|
|
32
32
|
logger = get_logger(__file__)
|
|
33
33
|
|
|
@@ -75,7 +75,7 @@ def get_analytic(id, **kwargs):
|
|
|
75
75
|
if not analytic_service.does_analytic_exist(id):
|
|
76
76
|
return not_found(err="Analytic does not exist")
|
|
77
77
|
|
|
78
|
-
return ok(analytic_service.get_analytic(id,
|
|
78
|
+
return ok(analytic_service.get_analytic(id, as_odm=False))
|
|
79
79
|
except ValueError as e:
|
|
80
80
|
return bad_request(err=str(e))
|
|
81
81
|
|
|
@@ -251,7 +251,7 @@ def delete_rule(id: str, user: User, **kwargs):
|
|
|
251
251
|
if not analytic_service.does_analytic_exist(id):
|
|
252
252
|
return not_found(err=f"Analytic {id} does not exist")
|
|
253
253
|
|
|
254
|
-
analytic
|
|
254
|
+
analytic = analytic_service.get_analytic(id, as_odm=True)
|
|
255
255
|
|
|
256
256
|
if not analytic.rule:
|
|
257
257
|
return bad_request(err="This is not a rule analytic, and cannot be deleted.")
|
|
@@ -304,7 +304,7 @@ def add_comment(id: str, user: dict[str, Any], **kwargs):
|
|
|
304
304
|
if not analytic_service.does_analytic_exist(id):
|
|
305
305
|
return not_found(err="Analytic %s does not exist" % id)
|
|
306
306
|
|
|
307
|
-
analytic
|
|
307
|
+
analytic = analytic_service.get_analytic(id, as_odm=True)
|
|
308
308
|
|
|
309
309
|
try:
|
|
310
310
|
analytic.comment.append(
|
|
@@ -321,8 +321,6 @@ def add_comment(id: str, user: dict[str, Any], **kwargs):
|
|
|
321
321
|
except DataStoreException as e:
|
|
322
322
|
return bad_request(err=str(e))
|
|
323
323
|
|
|
324
|
-
analytic = analytic_service.get_analytic(id)
|
|
325
|
-
|
|
326
324
|
return ok(analytic)
|
|
327
325
|
|
|
328
326
|
|
|
@@ -363,7 +361,7 @@ def edit_comment(id: str, comment_id: str, user: dict[str, Any], **kwargs):
|
|
|
363
361
|
if len(comment_data) > MAX_COMMENT_LEN:
|
|
364
362
|
return bad_request(err="Comment is too long.")
|
|
365
363
|
|
|
366
|
-
analytic
|
|
364
|
+
analytic = analytic_service.get_analytic(id, as_odm=True)
|
|
367
365
|
|
|
368
366
|
comment: Optional[Comment] = next((c for c in analytic.comment if c.id == comment_id), None)
|
|
369
367
|
|
|
@@ -420,14 +418,11 @@ def react_comment(id: str, comment_id: str, user: dict[str, Any], **kwargs):
|
|
|
420
418
|
if not analytic_service.does_analytic_exist(id):
|
|
421
419
|
return not_found(err=f"Analytic {id} does not exist")
|
|
422
420
|
|
|
423
|
-
analytic
|
|
421
|
+
analytic = analytic_service.get_analytic(id, as_odm=True)
|
|
424
422
|
|
|
425
423
|
for comment in analytic.comment:
|
|
426
424
|
if comment.id == comment_id:
|
|
427
|
-
comment["
|
|
428
|
-
**comment.get("reactions", {}),
|
|
429
|
-
user["uname"]: react_data,
|
|
430
|
-
}
|
|
425
|
+
comment.reactions[user["uname"]] = react_data
|
|
431
426
|
|
|
432
427
|
datastore().analytic.save(analytic.analytic_id, analytic)
|
|
433
428
|
|
|
@@ -455,11 +450,11 @@ def remove_react_comment(id: str, comment_id: str, user: dict[str, Any], **kwarg
|
|
|
455
450
|
if not analytic_service.does_analytic_exist(id):
|
|
456
451
|
return not_found(err=f"Analytic {id} does not exist")
|
|
457
452
|
|
|
458
|
-
analytic
|
|
453
|
+
analytic = analytic_service.get_analytic(id, as_odm=True)
|
|
459
454
|
|
|
460
455
|
for comment in analytic.comment:
|
|
461
456
|
if comment.id == comment_id:
|
|
462
|
-
reactions = comment.
|
|
457
|
+
reactions = comment.reactions
|
|
463
458
|
reactions.pop(user["uname"], None)
|
|
464
459
|
comment["reactions"] = {**reactions}
|
|
465
460
|
|
|
@@ -497,7 +492,7 @@ def delete_comments(id: str, user: User, **kwargs):
|
|
|
497
492
|
if len(comment_ids) == 0:
|
|
498
493
|
return bad_request(err="Supply at least one comment to delete.")
|
|
499
494
|
|
|
500
|
-
analytic
|
|
495
|
+
analytic = analytic_service.get_analytic(id, as_odm=True)
|
|
501
496
|
|
|
502
497
|
new_comments = []
|
|
503
498
|
for comment in analytic.comment:
|
|
@@ -548,7 +543,7 @@ def set_analytic_owner(id: str, user: dict[str, Any], **kwargs):
|
|
|
548
543
|
if not user_service.get_user(data["username"]):
|
|
549
544
|
return not_found(err=f"User {data['username']} does not exist")
|
|
550
545
|
|
|
551
|
-
analytic
|
|
546
|
+
analytic = analytic_service.get_analytic(id, as_odm=True)
|
|
552
547
|
|
|
553
548
|
analytic.owner = data["username"]
|
|
554
549
|
|
|
@@ -588,7 +583,7 @@ def set_as_favourite(id, **kwargs):
|
|
|
588
583
|
try:
|
|
589
584
|
current_user = storage.user.get_if_exists(kwargs["user"]["uname"])
|
|
590
585
|
|
|
591
|
-
current_user
|
|
586
|
+
current_user.favourite_analytics.append(id)
|
|
592
587
|
|
|
593
588
|
storage.user.save(current_user["uname"], current_user)
|
|
594
589
|
|
|
@@ -622,9 +617,7 @@ def remove_as_favourite(id, **kwargs):
|
|
|
622
617
|
try:
|
|
623
618
|
current_user = storage.user.get_if_exists(kwargs["user"]["uname"])
|
|
624
619
|
|
|
625
|
-
current_user["favourite_analytics"] = list(
|
|
626
|
-
filter(lambda f: f != id, current_user.get("favourite_analytics", []))
|
|
627
|
-
)
|
|
620
|
+
current_user["favourite_analytics"] = list(filter(lambda f: f != id, current_user.favourite_analytics))
|
|
628
621
|
|
|
629
622
|
storage.user.save(current_user["uname"], current_user)
|
|
630
623
|
|
|
@@ -674,7 +667,7 @@ def add_notebook(id: str, user: dict[str, Any], **kwargs):
|
|
|
674
667
|
if not analytic_service.does_analytic_exist(id):
|
|
675
668
|
return not_found(err="Analytic %s does not exist" % id)
|
|
676
669
|
|
|
677
|
-
analytic
|
|
670
|
+
analytic = analytic_service.get_analytic(id, as_odm=True)
|
|
678
671
|
|
|
679
672
|
try:
|
|
680
673
|
analytic.notebooks.append(
|
|
@@ -692,8 +685,6 @@ def add_notebook(id: str, user: dict[str, Any], **kwargs):
|
|
|
692
685
|
except DataStoreException as e:
|
|
693
686
|
return bad_request(err=str(e))
|
|
694
687
|
|
|
695
|
-
analytic = analytic_service.get_analytic(id)
|
|
696
|
-
|
|
697
688
|
return ok(analytic)
|
|
698
689
|
|
|
699
690
|
|
|
@@ -726,7 +717,7 @@ def delete_notebook(id: str, user: User, **kwargs):
|
|
|
726
717
|
if len(notebook_ids) == 0:
|
|
727
718
|
return bad_request(err="A notebook id is necessary for deletion.")
|
|
728
719
|
|
|
729
|
-
analytic
|
|
720
|
+
analytic = analytic_service.get_analytic(id, as_odm=True)
|
|
730
721
|
|
|
731
722
|
new_notebooks = []
|
|
732
723
|
for notebook in analytic.notebooks:
|
|
@@ -30,7 +30,7 @@ from howler.common.loader import datastore
|
|
|
30
30
|
from howler.common.logging import get_logger
|
|
31
31
|
from howler.common.swagger import generate_swagger_docs
|
|
32
32
|
from howler.config import config
|
|
33
|
-
from howler.odm.models.user import User
|
|
33
|
+
from howler.odm.models.user import ApiKey, User
|
|
34
34
|
from howler.security import api_login
|
|
35
35
|
from howler.security.utils import generate_random_secret
|
|
36
36
|
from howler.services import jwt_service
|
|
@@ -41,7 +41,7 @@ logger = get_logger(__file__)
|
|
|
41
41
|
|
|
42
42
|
SUB_API = "auth"
|
|
43
43
|
auth_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
44
|
-
auth_api._doc = "Allow user to authenticate to the web server"
|
|
44
|
+
auth_api._doc = "Allow user to authenticate to the web server" # type: ignore
|
|
45
45
|
|
|
46
46
|
logger = get_logger(__file__)
|
|
47
47
|
|
|
@@ -117,6 +117,7 @@ def add_apikey(**kwargs): # noqa: C901
|
|
|
117
117
|
)
|
|
118
118
|
max_expiry = datetime.fromtimestamp(data["exp"])
|
|
119
119
|
|
|
120
|
+
expiry = None
|
|
120
121
|
if expiry_date:
|
|
121
122
|
try:
|
|
122
123
|
expiry = datetime.fromisoformat(expiry_date.replace("Z", ""))
|
|
@@ -136,10 +137,10 @@ def add_apikey(**kwargs): # noqa: C901
|
|
|
136
137
|
"acl": privs,
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
if
|
|
140
|
+
if expiry:
|
|
140
141
|
new_key["expiry_date"] = expiry.isoformat()
|
|
141
142
|
|
|
142
|
-
user_data.apikeys[key_name] = new_key
|
|
143
|
+
user_data.apikeys[key_name] = ApiKey(new_key)
|
|
143
144
|
except HowlerException as e:
|
|
144
145
|
return bad_request(err=e.message)
|
|
145
146
|
|
|
@@ -235,8 +236,8 @@ def login(**_): # noqa: C901
|
|
|
235
236
|
apikey = data.get("apikey", None)
|
|
236
237
|
|
|
237
238
|
# These variables are what will eventually be returned, if authentication is successful
|
|
238
|
-
logged_in_uname = None
|
|
239
|
-
access_token = None
|
|
239
|
+
logged_in_uname: str | None = None
|
|
240
|
+
access_token: str | None = None
|
|
240
241
|
refresh_token = data.get("refresh_token", None)
|
|
241
242
|
priv: Optional[list[str]] = []
|
|
242
243
|
|
|
@@ -316,7 +317,7 @@ def login(**_): # noqa: C901
|
|
|
316
317
|
token_data, oauth_provider, skip_setup=False, access_token=access_token
|
|
317
318
|
)
|
|
318
319
|
|
|
319
|
-
logged_in_uname = cur_user
|
|
320
|
+
logged_in_uname = cur_user.uname
|
|
320
321
|
|
|
321
322
|
priv = ["R", "W", "E"]
|
|
322
323
|
|
|
@@ -336,7 +337,7 @@ def login(**_): # noqa: C901
|
|
|
336
337
|
if not user_data:
|
|
337
338
|
raise AuthenticationException("User does not exist, or authentication was invalid") # noqa: TRY301
|
|
338
339
|
|
|
339
|
-
logged_in_uname = user_data
|
|
340
|
+
logged_in_uname = user_data.uname
|
|
340
341
|
|
|
341
342
|
else:
|
|
342
343
|
raise AuthenticationException("Not enough information to proceed with authentication") # noqa: TRY301
|
|
@@ -366,9 +367,10 @@ def login(**_): # noqa: C901
|
|
|
366
367
|
|
|
367
368
|
# Generate the token this user can use to authenticate from now on
|
|
368
369
|
|
|
370
|
+
app_token = None
|
|
369
371
|
if access_token:
|
|
370
372
|
app_token = access_token
|
|
371
|
-
|
|
373
|
+
elif logged_in_uname:
|
|
372
374
|
app_token = f"{logged_in_uname}:{auth_service.create_token(logged_in_uname, typing.cast(list[str], priv))}"
|
|
373
375
|
|
|
374
376
|
return ok(
|
|
@@ -2,8 +2,8 @@ import sys
|
|
|
2
2
|
import time
|
|
3
3
|
from typing import Callable, Optional
|
|
4
4
|
|
|
5
|
-
import elasticapm
|
|
6
5
|
import requests
|
|
6
|
+
from elasticapm.traces import capture_span
|
|
7
7
|
from flask import request
|
|
8
8
|
|
|
9
9
|
from howler.api import bad_gateway, make_subapi_blueprint, ok
|
|
@@ -16,7 +16,7 @@ from howler.security import api_login
|
|
|
16
16
|
|
|
17
17
|
SUB_API = "clue"
|
|
18
18
|
clue_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
19
|
-
clue_api._doc = "Proxy enrichment requests to clue"
|
|
19
|
+
clue_api._doc = "Proxy enrichment requests to clue" # type: ignore
|
|
20
20
|
|
|
21
21
|
logger = get_logger(__file__)
|
|
22
22
|
|
|
@@ -74,7 +74,7 @@ def proxy_to_clue(path, **kwargs):
|
|
|
74
74
|
clue_token = get_token(auth_token)
|
|
75
75
|
|
|
76
76
|
start = time.perf_counter()
|
|
77
|
-
with
|
|
77
|
+
with capture_span("clue", span_type="http"):
|
|
78
78
|
if request.method.lower() == "get":
|
|
79
79
|
response = requests.get(
|
|
80
80
|
f"{config.core.clue.url}/{path}",
|
|
@@ -10,7 +10,7 @@ from howler.security.utils import get_disco_url
|
|
|
10
10
|
|
|
11
11
|
SUB_API = "configs"
|
|
12
12
|
config_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
13
|
-
config_api._doc = "Read configuration data about the system"
|
|
13
|
+
config_api._doc = "Read configuration data about the system" # type: ignore
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@generate_swagger_docs()
|
|
@@ -12,7 +12,7 @@ from howler.services import dossier_service
|
|
|
12
12
|
|
|
13
13
|
SUB_API = "dossier"
|
|
14
14
|
dossier_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
15
|
-
dossier_api._doc = "Manage the different dossiers created for filtering hits"
|
|
15
|
+
dossier_api._doc = "Manage the different dossiers created for filtering hits" # type: ignore
|
|
16
16
|
|
|
17
17
|
logger = get_logger(__file__)
|
|
18
18
|
|
|
@@ -7,7 +7,7 @@ SUB_API = "help"
|
|
|
7
7
|
classification_definition = CLASSIFICATION.get_parsed_classification_definition()
|
|
8
8
|
|
|
9
9
|
help_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
10
|
-
help_api._doc = "Provide information about the system configuration"
|
|
10
|
+
help_api._doc = "Provide information about the system configuration" # type: ignore
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@generate_swagger_docs()
|
|
@@ -36,7 +36,7 @@ MAX_COMMENT_LEN = 5000
|
|
|
36
36
|
|
|
37
37
|
SUB_API = "hit"
|
|
38
38
|
hit_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
39
|
-
hit_api._doc = "Manage the different hits in the system"
|
|
39
|
+
hit_api._doc = "Manage the different hits in the system" # type: ignore
|
|
40
40
|
|
|
41
41
|
FIELDS = Hit.flat_fields()
|
|
42
42
|
|
|
@@ -118,7 +118,7 @@ def create_hits(user: User, **kwargs):
|
|
|
118
118
|
# Ensure all ids are consistent
|
|
119
119
|
if odm.event is not None:
|
|
120
120
|
odm.event.id = odm.howler.id
|
|
121
|
-
hit_service.create_hit(odm.howler.id, odm, user=user
|
|
121
|
+
hit_service.create_hit(odm.howler.id, odm, user=user.uname)
|
|
122
122
|
analytic_service.save_from_hit(odm, user)
|
|
123
123
|
|
|
124
124
|
datastore().hit.commit()
|
|
@@ -311,12 +311,15 @@ def overwrite_hit(id: str, server_version: str, **kwargs):
|
|
|
311
311
|
return bad_request(err="The JSON payload must be a subset of a valid Hit object.")
|
|
312
312
|
|
|
313
313
|
try:
|
|
314
|
-
new_hit =
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
314
|
+
new_hit = cast(
|
|
315
|
+
dict[str, Any],
|
|
316
|
+
merge(
|
|
317
|
+
hit_service.flatten(hit.as_primitives(), odm=Hit),
|
|
318
|
+
hit_service.flatten(new_fields),
|
|
319
|
+
strategy=Strategy.REPLACE
|
|
320
|
+
if bool(request.args.get("replace", False, type=lambda v: v.lower() == "true"))
|
|
321
|
+
else Strategy.ADDITIVE,
|
|
322
|
+
),
|
|
320
323
|
)
|
|
321
324
|
|
|
322
325
|
new_hit, new_version = hit_service.save_hit(Hit(new_hit), server_version)
|
|
@@ -486,9 +489,9 @@ def get_assigned_hits(user, **kwargs):
|
|
|
486
489
|
deep_paging_id=request.args.get("deep_paging_id", None),
|
|
487
490
|
offset=request.args.get("offset", 0, type=int), # type: ignore[union-attr]
|
|
488
491
|
rows=request.args.get("rows", None, type=int), # type: ignore[union-attr]
|
|
489
|
-
sort=request.args.get("sort", None),
|
|
490
|
-
fl=request.args.get("fl", None),
|
|
491
|
-
timeout=request.args.get("timeout", None),
|
|
492
|
+
sort=request.args.get("sort", None, type=str),
|
|
493
|
+
fl=request.args.get("fl", None, type=str),
|
|
494
|
+
timeout=request.args.get("timeout", None, type=int),
|
|
492
495
|
as_obj=False,
|
|
493
496
|
)["items"]
|
|
494
497
|
|
|
@@ -554,7 +557,7 @@ def add_label(id, label_set, user, **kwargs):
|
|
|
554
557
|
user=user,
|
|
555
558
|
)
|
|
556
559
|
|
|
557
|
-
hit, version = hit_service.get_hit(id, version=True)
|
|
560
|
+
hit, version = hit_service.get_hit(id, as_odm=False, version=True)
|
|
558
561
|
|
|
559
562
|
return ok(hit), version
|
|
560
563
|
|
|
@@ -612,7 +615,7 @@ def remove_labels(id, label_set, user, **kwargs):
|
|
|
612
615
|
user=user,
|
|
613
616
|
)
|
|
614
617
|
|
|
615
|
-
hit, version = hit_service.get_hit(id, version=True)
|
|
618
|
+
hit, version = hit_service.get_hit(id, as_odm=False, version=True)
|
|
616
619
|
|
|
617
620
|
return ok(hit), version
|
|
618
621
|
|
|
@@ -672,7 +675,7 @@ def transition(id: str, user: User, **kwargs):
|
|
|
672
675
|
except HowlerException as e:
|
|
673
676
|
return internal_error(err=str(e))
|
|
674
677
|
|
|
675
|
-
hit, version = hit_service.get_hit(id, version=True)
|
|
678
|
+
hit, version = hit_service.get_hit(id, as_odm=False, version=True)
|
|
676
679
|
return ok(hit), version
|
|
677
680
|
|
|
678
681
|
|
|
@@ -759,7 +762,7 @@ def add_comment(id: str, user: dict[str, Any], **kwargs):
|
|
|
759
762
|
except DataStoreException as e:
|
|
760
763
|
return bad_request(err=str(e))
|
|
761
764
|
|
|
762
|
-
hit, version = hit_service.get_hit(id, version=True)
|
|
765
|
+
hit, version = hit_service.get_hit(id, as_odm=False, version=True)
|
|
763
766
|
|
|
764
767
|
return ok(hit), version
|
|
765
768
|
|
|
@@ -822,7 +825,7 @@ def edit_comment(id: str, comment_id: str, user: dict[str, Any], **kwargs):
|
|
|
822
825
|
if line[:3] not in ("+++", "---", "@@ "):
|
|
823
826
|
diff.append(line)
|
|
824
827
|
|
|
825
|
-
(
|
|
828
|
+
(new_hit, version) = hit_service.update_hit(
|
|
826
829
|
id,
|
|
827
830
|
[
|
|
828
831
|
hit_helper.list_remove("howler.comment", comment, silent=True),
|
|
@@ -834,7 +837,8 @@ def edit_comment(id: str, comment_id: str, user: dict[str, Any], **kwargs):
|
|
|
834
837
|
],
|
|
835
838
|
user["uname"],
|
|
836
839
|
)
|
|
837
|
-
|
|
840
|
+
|
|
841
|
+
return ok(new_hit), version
|
|
838
842
|
|
|
839
843
|
|
|
840
844
|
@generate_swagger_docs()
|
|
@@ -889,13 +893,14 @@ def delete_comments(id: str, user: User, **kwargs):
|
|
|
889
893
|
)
|
|
890
894
|
for comment in comments
|
|
891
895
|
],
|
|
892
|
-
user
|
|
896
|
+
user.uname,
|
|
893
897
|
)
|
|
894
898
|
|
|
895
899
|
except DataStoreException as e:
|
|
896
900
|
return bad_request(err=str(e))
|
|
897
|
-
|
|
898
|
-
|
|
901
|
+
|
|
902
|
+
new_hit, version = hit_service.get_hit(id, as_odm=False, version=True)
|
|
903
|
+
return ok(new_hit), version
|
|
899
904
|
|
|
900
905
|
|
|
901
906
|
@generate_swagger_docs()
|
|
@@ -1028,7 +1033,7 @@ def create_bundle(user: User, **kwargs):
|
|
|
1028
1033
|
if hit_id not in odm.howler.hits:
|
|
1029
1034
|
odm.howler.hits.append(hit_id)
|
|
1030
1035
|
|
|
1031
|
-
hit_service.create_hit(odm.howler.id, odm, user=user
|
|
1036
|
+
hit_service.create_hit(odm.howler.id, odm, user=user.uname)
|
|
1032
1037
|
analytic_service.save_from_hit(odm, user)
|
|
1033
1038
|
|
|
1034
1039
|
for hit_id in odm.howler.hits:
|
|
@@ -1039,9 +1044,7 @@ def create_bundle(user: User, **kwargs):
|
|
|
1039
1044
|
err=f"You cannot specify a bundle as a child of another bundle - {child_hit.howler.id} is a bundle."
|
|
1040
1045
|
)
|
|
1041
1046
|
|
|
1042
|
-
|
|
1043
|
-
new_bundle_list.append(odm.howler.id)
|
|
1044
|
-
child_hit.howler.bundles = new_bundle_list
|
|
1047
|
+
child_hit.howler.bundles.append(odm.howler.id)
|
|
1045
1048
|
datastore().hit.save(child_hit.howler.id, child_hit)
|
|
1046
1049
|
|
|
1047
1050
|
return created(odm)
|
|
@@ -1165,12 +1168,10 @@ def remove_bundle_children(id, **kwargs):
|
|
|
1165
1168
|
for hit_id in hit_ids:
|
|
1166
1169
|
child_hit: Hit = hit_service.get_hit(hit_id, as_odm=True)
|
|
1167
1170
|
|
|
1168
|
-
new_bundle_list = child_hit.howler.get("bundles", [])
|
|
1169
1171
|
try:
|
|
1170
|
-
|
|
1172
|
+
child_hit.howler.bundles.remove(bundle_hit.howler.id)
|
|
1171
1173
|
except ValueError:
|
|
1172
1174
|
logger.warning("Bundle isn't included in child %s!", bundle_hit.howler.id)
|
|
1173
|
-
child_hit.howler.bundles = new_bundle_list
|
|
1174
1175
|
|
|
1175
1176
|
datastore().hit.save(child_hit.howler.id, child_hit)
|
|
1176
1177
|
|
|
@@ -7,7 +7,7 @@ from howler.services import notebook_service
|
|
|
7
7
|
|
|
8
8
|
SUB_API = "notebook"
|
|
9
9
|
notebook_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
10
|
-
notebook_api._doc = "Get notebook information"
|
|
10
|
+
notebook_api._doc = "Get notebook information" # type: ignore
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@generate_swagger_docs()
|
|
@@ -21,7 +21,7 @@ from howler.utils.str_utils import sanitize_lucene_query
|
|
|
21
21
|
|
|
22
22
|
SUB_API = "overview"
|
|
23
23
|
overview_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
24
|
-
overview_api._doc = "Manage the different overviews created for viewing hits"
|
|
24
|
+
overview_api._doc = "Manage the different overviews created for viewing hits" # type: ignore
|
|
25
25
|
|
|
26
26
|
logger = get_logger(__file__)
|
|
27
27
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from copy import deepcopy
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from elasticsearch import BadRequestError
|
|
6
6
|
from elasticsearch._sync.client.indices import IndicesClient
|
|
7
|
-
from flask import request
|
|
7
|
+
from flask import Request, request
|
|
8
8
|
from sigma.backends.elasticsearch import LuceneBackend
|
|
9
9
|
from sigma.rule import SigmaRule
|
|
10
10
|
from werkzeug.exceptions import BadRequest
|
|
@@ -21,12 +21,12 @@ from howler.services import hit_service, lucene_service
|
|
|
21
21
|
|
|
22
22
|
SUB_API = "search"
|
|
23
23
|
search_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
24
|
-
search_api._doc = "Perform search queries"
|
|
24
|
+
search_api._doc = "Perform search queries" # type: ignore
|
|
25
25
|
|
|
26
26
|
logger = get_logger(__file__)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def generate_params(request, fields, multi_fields, params=None):
|
|
29
|
+
def generate_params(request: Request, fields: list[str], multi_fields: list[str], params: dict[str, Any] | None = None):
|
|
30
30
|
"""Generate a list of parameters, combining the request data and the query arguments"""
|
|
31
31
|
# I hate you, python
|
|
32
32
|
if params is None:
|
|
@@ -49,7 +49,7 @@ def generate_params(request, fields, multi_fields, params=None):
|
|
|
49
49
|
params = {
|
|
50
50
|
**params,
|
|
51
51
|
**{k: req_data[k] for k in fields if k in req_data},
|
|
52
|
-
**{k: req_data.getlist(k
|
|
52
|
+
**{k: req_data.getlist(k) for k in multi_fields if k in req_data},
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
return params, req_data
|
|
@@ -130,7 +130,6 @@ def search(index, **kwargs):
|
|
|
130
130
|
if has_access_control(index):
|
|
131
131
|
params.update({"access_control": user["access_control"]})
|
|
132
132
|
|
|
133
|
-
params["as_obj"] = False
|
|
134
133
|
params.update({"sort": (params.get("sort", None) or default_sort).split(",")})
|
|
135
134
|
|
|
136
135
|
query = req_data.get("query", None)
|
|
@@ -139,7 +138,7 @@ def search(index, **kwargs):
|
|
|
139
138
|
|
|
140
139
|
try:
|
|
141
140
|
metadata = params.pop("metadata", [])
|
|
142
|
-
result = collection().search(query, **params)
|
|
141
|
+
result = collection().search(query, as_obj=False, **params)
|
|
143
142
|
|
|
144
143
|
if index == "hit" and len(metadata) > 0:
|
|
145
144
|
hit_service.augment_metadata(result["items"], metadata, user)
|
|
@@ -703,7 +702,7 @@ def histogram(index, field, **kwargs):
|
|
|
703
702
|
|
|
704
703
|
# Get fields default values
|
|
705
704
|
field_info = collection().fields().get(field, None)
|
|
706
|
-
params: dict[str,
|
|
705
|
+
params: dict[str, Any] = {}
|
|
707
706
|
if field_info is None:
|
|
708
707
|
return bad_request(err=f"Field '{field}' is not a valid field in index: {index}")
|
|
709
708
|
elif field_info["type"] == "integer":
|
|
@@ -22,7 +22,7 @@ from howler.utils.str_utils import sanitize_lucene_query
|
|
|
22
22
|
|
|
23
23
|
SUB_API = "template"
|
|
24
24
|
template_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
25
|
-
template_api._doc = "Manage the different templates created for viewing hits"
|
|
25
|
+
template_api._doc = "Manage the different templates created for viewing hits" # type: ignore
|
|
26
26
|
|
|
27
27
|
logger = get_logger(__file__)
|
|
28
28
|
|
|
@@ -20,7 +20,7 @@ from howler.utils.uid import get_random_id
|
|
|
20
20
|
|
|
21
21
|
SUB_API = "tools"
|
|
22
22
|
tool_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
23
|
-
tool_api._doc = "Manage the tools"
|
|
23
|
+
tool_api._doc = "Manage the tools" # type: ignore
|
|
24
24
|
|
|
25
25
|
logger = get_logger(__file__)
|
|
26
26
|
|
|
@@ -167,12 +167,12 @@ def create_one_or_many_hits(tool_name: str, user: User, **kwargs): # noqa: C901
|
|
|
167
167
|
bundle_hit.howler.bundle_size += 1
|
|
168
168
|
odm.howler.bundles.append(bundle_hit.howler.id)
|
|
169
169
|
|
|
170
|
-
hit_service.create_hit(odm.howler.id, odm, user=user
|
|
170
|
+
hit_service.create_hit(odm.howler.id, odm, user=user.uname)
|
|
171
171
|
|
|
172
172
|
analytic_service.save_from_hit(odm, user)
|
|
173
173
|
|
|
174
174
|
if bundle_hit:
|
|
175
|
-
hit_service.create_hit(bundle_hit.howler.id, bundle_hit, user=user
|
|
175
|
+
hit_service.create_hit(bundle_hit.howler.id, bundle_hit, user=user.uname)
|
|
176
176
|
|
|
177
177
|
analytic_service.save_from_hit(bundle_hit, user)
|
|
178
178
|
|