howler-api 4.0.0.dev642__tar.gz → 4.0.0.dev657__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-4.0.0.dev642 → howler_api-4.0.0.dev657}/PKG-INFO +1 -1
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v2/case.py +18 -16
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/view.py +8 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/case_service.py +67 -57
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/pyproject.toml +1 -1
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/README.md +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/actions/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/actions/add_label.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/actions/change_field.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/actions/demote.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/actions/example_plugin.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/actions/prioritization.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/actions/promote.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/actions/remove_label.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/actions/transition.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/base.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/socket.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/action.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/analytic.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/auth.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/clue.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/configs.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/dossier.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/help.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/hit.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/notebook.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/overview.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/search.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/template.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/tool.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/user.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/utils/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/utils/etag.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v1/view.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v2/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v2/ingest.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/api/v2/search.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/app.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/README.md +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/classification.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/classification.yml +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/exceptions.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/loader.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/logging/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/logging/audit.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/logging/format.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/net.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/net_static.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/random_user.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/common/swagger.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/config.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/cronjobs/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/cronjobs/retention.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/cronjobs/view_cleanup.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/README.md +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/bulk.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/collection.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/constants.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/exceptions.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/howler_store.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/migrations/fix_process.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/operations.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/schemas.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/store.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/support/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/support/build.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/support/schemas.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/types.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/error.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/external/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/external/generate_mitre.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/external/generate_sigma_rules.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/external/generate_tlds.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/external/reindex_data.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/external/wipe_databases.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/gunicorn_config.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/healthz.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/helper/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/helper/azure.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/helper/discover.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/helper/hit.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/helper/oauth.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/helper/search.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/helper/workflow.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/helper/ws.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/README.md +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/base.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/charter.txt +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/constants.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/helper.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/howler_enum.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/action.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/analytic.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/assemblyline.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/aws.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/azure.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/case.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/cbs.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/clue.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/config.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/dossier.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/agent.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/autonomous_system.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/client.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/cloud.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/code_signature.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/container.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/dns.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/egress.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/elf.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/email.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/error.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/event.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/faas.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/file.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/geo.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/group.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/hash.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/host.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/http.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/ingress.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/interface.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/network.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/observer.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/organization.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/os.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/pe.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/process.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/registry.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/related.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/rule.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/server.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/threat.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/tls.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/url.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/user.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/user_agent.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/vulnerability.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/gcp.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/hit.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/howler_data.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/lead.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/localized_label.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/observable.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/overview.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/pivot.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/record.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/template.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/models/user.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/random_data.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/odm/randomizer.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/patched.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/plugins/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/plugins/config.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/README.md +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/counters.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/events.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/hash.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/lock.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/comms.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/multi.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/named.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/priority.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/set.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/remote/datatypes/user_quota_tracker.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/security/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/security/socket.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/security/utils.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/action_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/analytic_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/auth_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/config_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/docs_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/dossier_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/event_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/hit_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/jwt_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/lucene_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/notebook_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/observable_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/overview_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/search_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/template_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/services/user_service.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/__init__.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/annotations.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/chunk.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/compat.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/dict_utils.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/isotime.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/list_utils.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/lucene.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/path.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/socket_utils.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/str_utils.py +0 -0
- {howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/utils/uid.py +0 -0
|
@@ -7,6 +7,7 @@ from howler.common.loader import datastore
|
|
|
7
7
|
from howler.common.logging import get_logger
|
|
8
8
|
from howler.common.swagger import generate_swagger_docs
|
|
9
9
|
from howler.datastore.exceptions import DataStoreException
|
|
10
|
+
from howler.odm.models.case import CaseItem
|
|
10
11
|
from howler.odm.models.user import User
|
|
11
12
|
from howler.security import api_login
|
|
12
13
|
from howler.services import case_service
|
|
@@ -217,7 +218,8 @@ def append_item(id: str, user: User, **kwargs): # noqa: C901
|
|
|
217
218
|
Data Block:
|
|
218
219
|
{
|
|
219
220
|
"type": "hit", # Type of item to append: "hit", "observable", "case", "table", "lead", or "reference"
|
|
220
|
-
"value": "item-id-123" # The ID or reference value for the item
|
|
221
|
+
"value": "item-id-123" # The ID or reference value for the item,
|
|
222
|
+
"path": "example/path/Title"
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
Result Example:
|
|
@@ -230,29 +232,23 @@ def append_item(id: str, user: User, **kwargs): # noqa: C901
|
|
|
230
232
|
except UnsupportedMediaType:
|
|
231
233
|
return bad_request(err="Invalid JSON body")
|
|
232
234
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if "type" not in body:
|
|
237
|
-
return bad_request(err="Case 'type' missing")
|
|
235
|
+
for field in ["value", "type", "path"]:
|
|
236
|
+
if field not in body:
|
|
237
|
+
return bad_request(err=f"CaseItem '{field}' is required")
|
|
238
238
|
|
|
239
239
|
try:
|
|
240
|
-
case_service.append_case_item(
|
|
241
|
-
id, item_type=body["type"], item_value=body["value"], item_path=body.get("path", None)
|
|
242
|
-
)
|
|
240
|
+
return ok(case_service.append_case_item(id, item=CaseItem(body)))
|
|
243
241
|
except DataStoreException as e:
|
|
244
242
|
logger.exception("Save Error")
|
|
245
243
|
return internal_error(err=str(e))
|
|
246
244
|
except InvalidDataException as e:
|
|
247
245
|
return bad_request(err=str(e))
|
|
248
246
|
|
|
249
|
-
return ok()
|
|
250
|
-
|
|
251
247
|
|
|
252
248
|
@generate_swagger_docs()
|
|
253
|
-
@case_api.route("/<id>/items
|
|
249
|
+
@case_api.route("/<id>/items", methods=["DELETE"])
|
|
254
250
|
@api_login(required_priv=["R", "W"])
|
|
255
|
-
def delete_item(id: str,
|
|
251
|
+
def delete_item(id: str, **kwargs):
|
|
256
252
|
"""Delete an item from a case
|
|
257
253
|
|
|
258
254
|
This endpoint removes an item from a case's items list. If the item is a hit or
|
|
@@ -261,21 +257,27 @@ def delete_item(id: str, value: str, **kwargs):
|
|
|
261
257
|
|
|
262
258
|
Variables:
|
|
263
259
|
id => The id of the case to modify
|
|
264
|
-
value => The value of the item to delete (must match the item's value field)
|
|
265
260
|
|
|
266
261
|
Arguments:
|
|
267
262
|
None
|
|
268
263
|
|
|
269
264
|
Data Block:
|
|
270
|
-
|
|
265
|
+
{
|
|
266
|
+
"value": "item-id-123" # The value of the item to delete
|
|
267
|
+
}
|
|
271
268
|
|
|
272
269
|
Result Example:
|
|
273
270
|
{
|
|
274
271
|
"success": true # Did the deletion succeed?
|
|
275
272
|
}
|
|
276
273
|
"""
|
|
274
|
+
body = request.json
|
|
275
|
+
|
|
276
|
+
if not body or not isinstance(body, dict) or "value" not in body:
|
|
277
|
+
return bad_request(err="Request body must be a JSON object with a 'value' field.")
|
|
278
|
+
|
|
277
279
|
try:
|
|
278
|
-
case_service.remove_case_item(id, item_value=value)
|
|
280
|
+
case_service.remove_case_item(id, item_value=body["value"])
|
|
279
281
|
except DataStoreException as e:
|
|
280
282
|
logger.exception("Save Error")
|
|
281
283
|
return internal_error(err=str(e))
|
|
@@ -11,9 +11,17 @@ class Settings(odm.Model):
|
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
DEFAULT_INDEXES = ["hit"]
|
|
15
|
+
|
|
16
|
+
|
|
14
17
|
@odm.model(index=True, store=True, description="Model of views")
|
|
15
18
|
class View(odm.Model):
|
|
16
19
|
view_id: str = odm.UUID(description="A UUID for this view")
|
|
20
|
+
indexes: list[str] = odm.List(
|
|
21
|
+
odm.Keyword(),
|
|
22
|
+
default=DEFAULT_INDEXES,
|
|
23
|
+
description="What indexes this view applies to.",
|
|
24
|
+
)
|
|
17
25
|
title: str = odm.CaseInsensitiveKeyword(description="The name of this view.")
|
|
18
26
|
query: str = odm.Keyword(description="The query to run in this view.")
|
|
19
27
|
sort: str = odm.Keyword(description="The sorting to use with this view.", optional=True)
|
|
@@ -222,13 +222,13 @@ def update_case(case_id: str, case_data: dict[str, Any], user: User) -> Case:
|
|
|
222
222
|
|
|
223
223
|
|
|
224
224
|
@overload
|
|
225
|
-
def append_case_item(case_id: str, item: CaseItem): ...
|
|
225
|
+
def append_case_item(case_id: str, item: CaseItem) -> Case: ...
|
|
226
226
|
|
|
227
227
|
|
|
228
228
|
@overload
|
|
229
229
|
def append_case_item(
|
|
230
230
|
case_id: str, item: None = None, item_type: str = ..., item_value: str = ..., item_path: str = ...
|
|
231
|
-
): ...
|
|
231
|
+
) -> Case: ...
|
|
232
232
|
|
|
233
233
|
|
|
234
234
|
def append_case_item( # noqa: C901
|
|
@@ -236,8 +236,8 @@ def append_case_item( # noqa: C901
|
|
|
236
236
|
item: CaseItem | None = None,
|
|
237
237
|
item_type: str | None = None,
|
|
238
238
|
item_value: str | None = None,
|
|
239
|
-
item_path: str = "related
|
|
240
|
-
):
|
|
239
|
+
item_path: str = "related",
|
|
240
|
+
) -> Case:
|
|
241
241
|
"""Append an item to a case, dispatching to the appropriate handler based on item type.
|
|
242
242
|
|
|
243
243
|
Can be called either with a pre-built CaseItem object or with individual
|
|
@@ -251,12 +251,13 @@ def append_case_item( # noqa: C901
|
|
|
251
251
|
"table", "lead", "reference"). Required if item is not provided.
|
|
252
252
|
item_value: The value/identifier of the item to append. Required if item
|
|
253
253
|
is not provided.
|
|
254
|
-
item_path:
|
|
255
|
-
|
|
254
|
+
item_path: Path for organizing the item within the case. Must not end
|
|
255
|
+
with a trailing "/".
|
|
256
256
|
|
|
257
257
|
Raises:
|
|
258
258
|
InvalidDataException: If item is not provided and item_type or item_value
|
|
259
|
-
are missing, or if item_type is not a valid CaseItemTypes value
|
|
259
|
+
are missing, or if item_type is not a valid CaseItemTypes value, or
|
|
260
|
+
if the resolved item path ends with a trailing "/".
|
|
260
261
|
"""
|
|
261
262
|
if item is None:
|
|
262
263
|
if not all([item_type, item_value]):
|
|
@@ -266,28 +267,31 @@ def append_case_item( # noqa: C901
|
|
|
266
267
|
raise InvalidDataException(f"Invalid item type: {item_type}, valid types are: {', '.join(CaseItemTypes)}")
|
|
267
268
|
|
|
268
269
|
if not item_path:
|
|
269
|
-
item_path = "related
|
|
270
|
+
item_path = "related"
|
|
270
271
|
|
|
271
272
|
item = CaseItem({"type": item_type, "value": item_value, "path": item_path})
|
|
272
273
|
|
|
274
|
+
if item.path.endswith("/"):
|
|
275
|
+
raise InvalidDataException("item path must not end with a trailing '/'")
|
|
276
|
+
|
|
273
277
|
match item.type:
|
|
274
278
|
case CaseItemTypes.HIT:
|
|
275
|
-
append_hit(case_id, item)
|
|
279
|
+
return append_hit(case_id, item)
|
|
276
280
|
case CaseItemTypes.OBSERVABLE:
|
|
277
|
-
append_observable(case_id, item)
|
|
281
|
+
return append_observable(case_id, item)
|
|
278
282
|
case CaseItemTypes.CASE:
|
|
279
|
-
append_case(case_id, item)
|
|
283
|
+
return append_case(case_id, item)
|
|
280
284
|
case CaseItemTypes.TABLE:
|
|
281
|
-
append_table(case_id, item)
|
|
285
|
+
return append_table(case_id, item)
|
|
282
286
|
case CaseItemTypes.LEAD:
|
|
283
|
-
append_lead(case_id, item)
|
|
287
|
+
return append_lead(case_id, item)
|
|
284
288
|
case CaseItemTypes.REFERENCE:
|
|
285
|
-
append_reference(case_id, item)
|
|
289
|
+
return append_reference(case_id, item)
|
|
286
290
|
case _:
|
|
287
291
|
raise InvalidDataException(f"Unsupported item type: {item_type}")
|
|
288
292
|
|
|
289
293
|
|
|
290
|
-
def append_hit(case_id: str, item: CaseItem):
|
|
294
|
+
def append_hit(case_id: str, item: CaseItem) -> Case:
|
|
291
295
|
"""Append a hit item to a case and create a back-reference on the hit.
|
|
292
296
|
|
|
293
297
|
Validates that the case and hit both exist and that the hit is not already
|
|
@@ -306,37 +310,30 @@ def append_hit(case_id: str, item: CaseItem):
|
|
|
306
310
|
"""
|
|
307
311
|
ds = datastore()
|
|
308
312
|
|
|
309
|
-
|
|
313
|
+
_case = ds.case.get(case_id)
|
|
310
314
|
|
|
311
|
-
if
|
|
315
|
+
if _case is None:
|
|
312
316
|
raise NotFoundException(f"Case {case_id} does not exist")
|
|
313
317
|
|
|
314
|
-
if any(item.value == case_item["value"] for case_item in
|
|
318
|
+
if any(item.value == case_item["value"] for case_item in _case.items):
|
|
315
319
|
raise InvalidDataException(f"Hit {item.value} already exists in case {case_id}")
|
|
316
320
|
|
|
317
|
-
hit = ds.hit.get(
|
|
321
|
+
hit = ds.hit.get(item.value)
|
|
318
322
|
|
|
319
|
-
|
|
320
|
-
raise NotFoundException(f"Hit {item.value} not found, cannot be added to case")
|
|
321
|
-
|
|
322
|
-
if item.path == "related/":
|
|
323
|
-
item.path = f"alerts/{hit.howler.analytic} ({hit.howler.id})"
|
|
323
|
+
_case.items.append(item)
|
|
324
324
|
|
|
325
|
-
|
|
325
|
+
_add_backreference(hit, _case.case_id)
|
|
326
326
|
|
|
327
|
-
|
|
328
|
-
raise DataStoreException(f"Failed to save {case.case_id} with new item {item.value}")
|
|
327
|
+
_sync_case_metadata(_case.case_id)
|
|
329
328
|
|
|
330
|
-
|
|
331
|
-
_sync_case_metadata(case_id)
|
|
329
|
+
return _case
|
|
332
330
|
|
|
333
331
|
|
|
334
|
-
def append_observable(case_id: str, item: CaseItem):
|
|
332
|
+
def append_observable(case_id: str, item: CaseItem) -> Case:
|
|
335
333
|
"""Append an observable item to a case and create a back-reference on the observable.
|
|
336
334
|
|
|
337
335
|
Validates that the case and observable both exist and that the observable is
|
|
338
|
-
not already present in the case.
|
|
339
|
-
observable's ID, then persists the updated case and adds a back-reference
|
|
336
|
+
not already present in the case. It then persists the updated case and adds a back-reference
|
|
340
337
|
from the observable to the case.
|
|
341
338
|
|
|
342
339
|
Args:
|
|
@@ -363,9 +360,6 @@ def append_observable(case_id: str, item: CaseItem):
|
|
|
363
360
|
if observable is None:
|
|
364
361
|
raise NotFoundException(f"Observable {item.value} not found, cannot be added to case")
|
|
365
362
|
|
|
366
|
-
if item.path == "related/":
|
|
367
|
-
item.path = f"observables/{observable.howler.id}"
|
|
368
|
-
|
|
369
363
|
_case.items.append(item)
|
|
370
364
|
|
|
371
365
|
if not datastore().case.save(_case.case_id, _case):
|
|
@@ -374,13 +368,14 @@ def append_observable(case_id: str, item: CaseItem):
|
|
|
374
368
|
_add_backreference(observable, _case.case_id)
|
|
375
369
|
_sync_case_metadata(case_id)
|
|
376
370
|
|
|
371
|
+
return _case
|
|
377
372
|
|
|
378
|
-
|
|
373
|
+
|
|
374
|
+
def append_case(case_id: str, item: CaseItem) -> Case:
|
|
379
375
|
"""Append a case reference item to a case.
|
|
380
376
|
|
|
381
377
|
Validates that both the parent case and the referenced case exist, and that
|
|
382
|
-
the referenced case is not already present in the parent case.
|
|
383
|
-
item's path to include the referenced case's ID, then persists the updated
|
|
378
|
+
the referenced case is not already present in the parent case. It then persists the updated
|
|
384
379
|
parent case.
|
|
385
380
|
|
|
386
381
|
Args:
|
|
@@ -394,7 +389,7 @@ def append_case(case_id: str, item: CaseItem):
|
|
|
394
389
|
"""
|
|
395
390
|
ds = datastore()
|
|
396
391
|
|
|
397
|
-
_case = ds.case.get(
|
|
392
|
+
_case = ds.case.get(case_id)
|
|
398
393
|
|
|
399
394
|
if _case is None:
|
|
400
395
|
raise NotFoundException(f"Case {case_id} does not exist")
|
|
@@ -407,18 +402,15 @@ def append_case(case_id: str, item: CaseItem):
|
|
|
407
402
|
if referenced_case is None:
|
|
408
403
|
raise NotFoundException(f"Referenced case {item.value} not found, cannot be added to case")
|
|
409
404
|
|
|
410
|
-
if item.path == "related/":
|
|
411
|
-
item.path = "cases/"
|
|
412
|
-
|
|
413
|
-
item.path += f"{referenced_case.case_id}"
|
|
414
|
-
|
|
415
405
|
_case.items.append(item)
|
|
416
406
|
|
|
417
407
|
if not datastore().case.save(_case.case_id, _case):
|
|
418
408
|
raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
|
|
419
409
|
|
|
410
|
+
return _case
|
|
411
|
+
|
|
420
412
|
|
|
421
|
-
def append_table(case_id: str, item: CaseItem):
|
|
413
|
+
def append_table(case_id: str, item: CaseItem) -> Case:
|
|
422
414
|
"""Append a table item to a case.
|
|
423
415
|
|
|
424
416
|
Not yet implemented.
|
|
@@ -433,7 +425,7 @@ def append_table(case_id: str, item: CaseItem):
|
|
|
433
425
|
raise NotImplementedError
|
|
434
426
|
|
|
435
427
|
|
|
436
|
-
def append_lead(case_id: str, item: CaseItem):
|
|
428
|
+
def append_lead(case_id: str, item: CaseItem) -> Case:
|
|
437
429
|
"""Append a lead item to a case.
|
|
438
430
|
|
|
439
431
|
Not yet implemented.
|
|
@@ -448,19 +440,37 @@ def append_lead(case_id: str, item: CaseItem):
|
|
|
448
440
|
raise NotImplementedError
|
|
449
441
|
|
|
450
442
|
|
|
451
|
-
def append_reference(case_id: str, item: CaseItem):
|
|
452
|
-
"""Append
|
|
443
|
+
def append_reference(case_id: str, item: CaseItem) -> Case:
|
|
444
|
+
"""Append an external reference item to a case.
|
|
453
445
|
|
|
454
|
-
|
|
446
|
+
Validates that the case exists and that the reference URL is not already
|
|
447
|
+
present in the case. It then persists the updated case.
|
|
455
448
|
|
|
456
449
|
Args:
|
|
457
450
|
case_id: Unique identifier of the case to append the reference to.
|
|
458
|
-
item: A CaseItem
|
|
451
|
+
item: A CaseItem whose ``value`` is the external URL to reference.
|
|
459
452
|
|
|
460
453
|
Raises:
|
|
461
|
-
|
|
454
|
+
NotFoundException: If the case does not exist.
|
|
455
|
+
InvalidDataException: If the reference URL is already present in the case.
|
|
456
|
+
DataStoreException: If saving the updated case fails.
|
|
462
457
|
"""
|
|
463
|
-
|
|
458
|
+
ds = datastore()
|
|
459
|
+
|
|
460
|
+
_case = ds.case.get_if_exists(key=case_id, as_obj=True)
|
|
461
|
+
|
|
462
|
+
if _case is None:
|
|
463
|
+
raise NotFoundException(f"Case {case_id} does not exist")
|
|
464
|
+
|
|
465
|
+
if any(item.value == case_item["value"] for case_item in _case.items):
|
|
466
|
+
raise InvalidDataException(f"Reference {item.value} already exists in case {case_id}")
|
|
467
|
+
|
|
468
|
+
_case.items.append(item)
|
|
469
|
+
|
|
470
|
+
if not datastore().case.save(_case.case_id, _case):
|
|
471
|
+
raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
|
|
472
|
+
|
|
473
|
+
return _case
|
|
464
474
|
|
|
465
475
|
|
|
466
476
|
def _collect_indicators_from_related(related: Related | None) -> set[str]:
|
|
@@ -597,15 +607,15 @@ def remove_case_item(case_id: str, item_value: str):
|
|
|
597
607
|
if not _case:
|
|
598
608
|
raise NotFoundException(f"Case {case_id} does not exist")
|
|
599
609
|
|
|
600
|
-
|
|
601
|
-
if not
|
|
610
|
+
item = next((_item for _item in _case.items if _item["value"] == item_value), None)
|
|
611
|
+
if not item:
|
|
602
612
|
raise NotFoundException(f"Case item {item_value} does not exist")
|
|
603
613
|
|
|
604
614
|
backing_obj: Hit | Observable | None = None
|
|
605
|
-
if
|
|
606
|
-
backing_obj = ds[
|
|
615
|
+
if item.type in [CaseItemTypes.HIT, CaseItemTypes.OBSERVABLE]:
|
|
616
|
+
backing_obj = ds[item.type].get(item.value)
|
|
607
617
|
|
|
608
|
-
_case.items.remove(
|
|
618
|
+
_case.items.remove(item)
|
|
609
619
|
|
|
610
620
|
if not ds.case.save(_case.case_id, _case):
|
|
611
621
|
raise DataStoreException("Failed to save case after item removal")
|
|
@@ -152,7 +152,7 @@ suppress-none-returning = true
|
|
|
152
152
|
[tool.poetry]
|
|
153
153
|
package-mode = true
|
|
154
154
|
name = "howler-api"
|
|
155
|
-
version = "4.0.0.
|
|
155
|
+
version = "4.0.0.dev657"
|
|
156
156
|
description = "Howler - API server"
|
|
157
157
|
authors = [
|
|
158
158
|
"Canadian Centre for Cyber Security <howler@cyber.gc.ca>",
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{howler_api-4.0.0.dev642 → howler_api-4.0.0.dev657}/howler/datastore/migrations/fix_process.py
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|