howler-api 4.0.0.dev657__tar.gz → 4.0.0.dev669__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.dev657 → howler_api-4.0.0.dev669}/PKG-INFO +1 -1
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v2/case.py +58 -12
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/case_service.py +90 -18
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/pyproject.toml +1 -1
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/README.md +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/actions/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/actions/add_label.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/actions/change_field.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/actions/demote.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/actions/example_plugin.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/actions/prioritization.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/actions/promote.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/actions/remove_label.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/actions/transition.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/base.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/socket.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/action.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/analytic.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/auth.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/clue.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/configs.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/dossier.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/help.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/hit.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/notebook.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/overview.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/search.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/template.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/tool.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/user.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/utils/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/utils/etag.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v1/view.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v2/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v2/ingest.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/api/v2/search.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/app.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/README.md +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/classification.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/classification.yml +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/exceptions.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/loader.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/logging/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/logging/audit.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/logging/format.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/net.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/net_static.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/random_user.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/common/swagger.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/config.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/cronjobs/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/cronjobs/retention.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/cronjobs/view_cleanup.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/README.md +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/bulk.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/collection.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/constants.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/exceptions.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/howler_store.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/migrations/fix_process.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/operations.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/schemas.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/store.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/support/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/support/build.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/support/schemas.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/datastore/types.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/error.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/external/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/external/generate_mitre.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/external/generate_sigma_rules.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/external/generate_tlds.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/external/reindex_data.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/external/wipe_databases.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/gunicorn_config.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/healthz.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/helper/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/helper/azure.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/helper/discover.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/helper/hit.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/helper/oauth.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/helper/search.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/helper/workflow.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/helper/ws.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/README.md +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/base.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/charter.txt +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/constants.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/helper.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/howler_enum.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/action.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/analytic.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/assemblyline.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/aws.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/azure.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/case.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/cbs.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/clue.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/config.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/dossier.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/agent.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/autonomous_system.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/client.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/cloud.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/code_signature.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/container.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/dns.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/egress.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/elf.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/email.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/error.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/event.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/faas.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/file.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/geo.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/group.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/hash.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/host.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/http.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/ingress.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/interface.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/network.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/observer.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/organization.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/os.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/pe.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/process.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/registry.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/related.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/rule.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/server.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/threat.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/tls.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/url.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/user.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/user_agent.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/vulnerability.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/gcp.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/hit.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/howler_data.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/lead.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/localized_label.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/observable.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/overview.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/pivot.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/record.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/template.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/user.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/view.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/random_data.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/odm/randomizer.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/patched.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/plugins/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/plugins/config.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/README.md +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/counters.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/events.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/hash.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/lock.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/queues/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/queues/comms.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/queues/multi.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/queues/named.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/queues/priority.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/set.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/user_quota_tracker.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/security/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/security/socket.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/security/utils.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/action_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/analytic_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/auth_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/config_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/docs_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/dossier_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/event_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/hit_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/jwt_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/lucene_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/notebook_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/observable_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/overview_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/search_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/template_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/services/user_service.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/__init__.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/annotations.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/chunk.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/compat.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/dict_utils.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/isotime.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/list_utils.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/lucene.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/path.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/socket_utils.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/str_utils.py +0 -0
- {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/utils/uid.py +0 -0
|
@@ -246,42 +246,88 @@ def append_item(id: str, user: User, **kwargs): # noqa: C901
|
|
|
246
246
|
|
|
247
247
|
|
|
248
248
|
@generate_swagger_docs()
|
|
249
|
-
@case_api.route("/<
|
|
249
|
+
@case_api.route("/<case_id>/items", methods=["DELETE"])
|
|
250
250
|
@api_login(required_priv=["R", "W"])
|
|
251
|
-
def delete_item(
|
|
252
|
-
"""Delete
|
|
251
|
+
def delete_item(case_id: str, **kwargs):
|
|
252
|
+
"""Delete one or more items from a case
|
|
253
253
|
|
|
254
|
-
This endpoint removes
|
|
254
|
+
This endpoint removes items from a case's items list. If an item is a hit or
|
|
255
255
|
observable, the bidirectional relationship is cleaned up - the case reference will
|
|
256
256
|
be removed from the backing object's related.cases list.
|
|
257
257
|
|
|
258
258
|
Variables:
|
|
259
|
-
|
|
259
|
+
case_id => The id of the case to modify
|
|
260
260
|
|
|
261
261
|
Arguments:
|
|
262
262
|
None
|
|
263
263
|
|
|
264
264
|
Data Block:
|
|
265
265
|
{
|
|
266
|
-
"
|
|
266
|
+
"values": ["item-id-123", "item-id-456"] # The values of the items to delete
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
Result Example:
|
|
270
270
|
{
|
|
271
|
-
|
|
271
|
+
...case # The updated case data
|
|
272
272
|
}
|
|
273
273
|
"""
|
|
274
274
|
body = request.json
|
|
275
275
|
|
|
276
|
-
if not body or not isinstance(body, dict) or "
|
|
277
|
-
return bad_request(err="Request body must be a JSON object with a '
|
|
276
|
+
if not body or not isinstance(body, dict) or "values" not in body:
|
|
277
|
+
return bad_request(err="Request body must be a JSON object with a 'values' field.")
|
|
278
|
+
|
|
279
|
+
values = body["values"]
|
|
280
|
+
if not isinstance(values, list) or not values:
|
|
281
|
+
return bad_request(err="'values' must be a non-empty list.")
|
|
278
282
|
|
|
279
283
|
try:
|
|
280
|
-
case_service.
|
|
284
|
+
return ok(case_service.remove_case_items(case_id, values))
|
|
281
285
|
except DataStoreException as e:
|
|
282
286
|
logger.exception("Save Error")
|
|
283
287
|
return internal_error(err=str(e))
|
|
284
|
-
except InvalidDataException as e:
|
|
288
|
+
except (InvalidDataException, NotFoundException) as e:
|
|
285
289
|
return bad_request(err=str(e))
|
|
286
290
|
|
|
287
|
-
|
|
291
|
+
|
|
292
|
+
@generate_swagger_docs()
|
|
293
|
+
@case_api.route("/<case_id>/items", methods=["PATCH"])
|
|
294
|
+
@api_login(required_priv=["R", "W"])
|
|
295
|
+
def rename_item(case_id: str, **kwargs):
|
|
296
|
+
"""Rename (re-path) an item within a case
|
|
297
|
+
|
|
298
|
+
Updates the path of a single item identified by its value. The new path must
|
|
299
|
+
not already be used by another item in the case.
|
|
300
|
+
|
|
301
|
+
Variables:
|
|
302
|
+
case_id => The id of the case to modify
|
|
303
|
+
|
|
304
|
+
Arguments:
|
|
305
|
+
None
|
|
306
|
+
|
|
307
|
+
Data Block:
|
|
308
|
+
{
|
|
309
|
+
"value": "item-id-123", # The value of the item to rename
|
|
310
|
+
"new_path": "folder/New Name" # The new path for the item
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
Result Example:
|
|
314
|
+
{
|
|
315
|
+
...case # The updated case data
|
|
316
|
+
}
|
|
317
|
+
"""
|
|
318
|
+
body = request.json
|
|
319
|
+
|
|
320
|
+
if not body or not isinstance(body, dict):
|
|
321
|
+
return bad_request(err="Request body must be a JSON object.")
|
|
322
|
+
|
|
323
|
+
for field in ["value", "new_path"]:
|
|
324
|
+
if field not in body:
|
|
325
|
+
return bad_request(err=f"'{field}' is required.")
|
|
326
|
+
|
|
327
|
+
try:
|
|
328
|
+
return ok(case_service.rename_case_item(case_id, item_value=body["value"], new_path=body["new_path"]))
|
|
329
|
+
except DataStoreException as e:
|
|
330
|
+
logger.exception("Save Error")
|
|
331
|
+
return internal_error(err=str(e))
|
|
332
|
+
except (InvalidDataException, NotFoundException) as e:
|
|
333
|
+
return bad_request(err=str(e))
|
|
@@ -320,8 +320,14 @@ def append_hit(case_id: str, item: CaseItem) -> Case:
|
|
|
320
320
|
|
|
321
321
|
hit = ds.hit.get(item.value)
|
|
322
322
|
|
|
323
|
+
if hit is None:
|
|
324
|
+
raise NotFoundException(f"Hit {item.value} not found, cannot be added to case")
|
|
325
|
+
|
|
323
326
|
_case.items.append(item)
|
|
324
327
|
|
|
328
|
+
if not ds.case.save(_case.case_id, _case):
|
|
329
|
+
raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
|
|
330
|
+
|
|
325
331
|
_add_backreference(hit, _case.case_id)
|
|
326
332
|
|
|
327
333
|
_sync_case_metadata(_case.case_id)
|
|
@@ -362,7 +368,7 @@ def append_observable(case_id: str, item: CaseItem) -> Case:
|
|
|
362
368
|
|
|
363
369
|
_case.items.append(item)
|
|
364
370
|
|
|
365
|
-
if not
|
|
371
|
+
if not ds.case.save(_case.case_id, _case):
|
|
366
372
|
raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
|
|
367
373
|
|
|
368
374
|
_add_backreference(observable, _case.case_id)
|
|
@@ -585,20 +591,26 @@ def remove_backreference(backing_obj: Hit | Observable | None, case_id: str):
|
|
|
585
591
|
datastore()[backing_obj.__class__.__name__.lower()].save(backing_obj.howler.id, backing_obj)
|
|
586
592
|
|
|
587
593
|
|
|
588
|
-
def
|
|
589
|
-
"""Remove
|
|
594
|
+
def remove_case_items(case_id: str, values: list[str]):
|
|
595
|
+
"""Remove one or more items from a case in a single atomic operation.
|
|
590
596
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
the
|
|
597
|
+
Validates that every requested value exists within the case before making
|
|
598
|
+
any modifications. If any value is missing the call raises NotFoundException
|
|
599
|
+
without altering the case. When all values are confirmed, all matching
|
|
600
|
+
items are removed in memory, the case is persisted once, and back-references
|
|
601
|
+
are cleaned up from any associated hits or observables.
|
|
594
602
|
|
|
595
603
|
Args:
|
|
596
|
-
case_id: Unique identifier of the case to remove
|
|
597
|
-
|
|
604
|
+
case_id: Unique identifier of the case to remove items from.
|
|
605
|
+
item_values: List of item values (IDs / URLs) to remove.
|
|
606
|
+
|
|
607
|
+
Returns:
|
|
608
|
+
The updated Case object.
|
|
598
609
|
|
|
599
610
|
Raises:
|
|
600
|
-
NotFoundException: If the case does not exist
|
|
601
|
-
|
|
611
|
+
NotFoundException: If the case does not exist, or if any requested value
|
|
612
|
+
is not present in the case's items list.
|
|
613
|
+
DataStoreException: If persisting the updated case fails.
|
|
602
614
|
"""
|
|
603
615
|
ds = datastore()
|
|
604
616
|
|
|
@@ -607,20 +619,80 @@ def remove_case_item(case_id: str, item_value: str):
|
|
|
607
619
|
if not _case:
|
|
608
620
|
raise NotFoundException(f"Case {case_id} does not exist")
|
|
609
621
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
622
|
+
# Build a lookup of value → item for all items currently in the case.
|
|
623
|
+
items_by_value = {_item["value"]: _item for _item in _case.items}
|
|
624
|
+
|
|
625
|
+
# Pre-validate all requested values before touching anything.
|
|
626
|
+
missing = [v for v in values if v not in items_by_value]
|
|
627
|
+
if missing:
|
|
628
|
+
raise NotFoundException(f"Case item(s) not found in case: {', '.join(missing)}")
|
|
613
629
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
630
|
+
# Resolve items and collect backing objects that need back-reference cleanup.
|
|
631
|
+
items_to_remove = [items_by_value[v] for v in values]
|
|
632
|
+
backing_objs: list[Hit | Observable] = []
|
|
633
|
+
for item in items_to_remove:
|
|
634
|
+
if item.type in [CaseItemTypes.HIT, CaseItemTypes.OBSERVABLE]:
|
|
635
|
+
obj = ds[item.type].get(item.value)
|
|
636
|
+
if obj:
|
|
637
|
+
backing_objs.append(obj)
|
|
617
638
|
|
|
618
|
-
|
|
639
|
+
# Remove all items in memory, then persist the case once.
|
|
640
|
+
for item in items_to_remove:
|
|
641
|
+
_case.items.remove(item)
|
|
619
642
|
|
|
620
643
|
if not ds.case.save(_case.case_id, _case):
|
|
621
644
|
raise DataStoreException("Failed to save case after item removal")
|
|
622
645
|
|
|
623
|
-
|
|
646
|
+
# Clean up back-references after the case is safely persisted.
|
|
647
|
+
for backing_obj in backing_objs:
|
|
624
648
|
remove_backreference(backing_obj, _case.case_id)
|
|
625
649
|
|
|
626
650
|
_sync_case_metadata(case_id)
|
|
651
|
+
|
|
652
|
+
return _case
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def rename_case_item(case_id: str, item_value: str, new_path: str) -> Case:
|
|
656
|
+
"""Rename (re-path) a single item within a case.
|
|
657
|
+
|
|
658
|
+
Validates that the target item exists in the case, that the new path is not
|
|
659
|
+
already used by another item, then updates the item's path in memory and
|
|
660
|
+
persists the case once.
|
|
661
|
+
|
|
662
|
+
Args:
|
|
663
|
+
case_id: Unique identifier of the case to modify.
|
|
664
|
+
item_value: The value/identifier of the item to rename.
|
|
665
|
+
new_path: The new path for the item (must not end with '/').
|
|
666
|
+
|
|
667
|
+
Returns:
|
|
668
|
+
The updated Case object.
|
|
669
|
+
|
|
670
|
+
Raises:
|
|
671
|
+
NotFoundException: If the case does not exist or the item is not found.
|
|
672
|
+
InvalidDataException: If new_path ends with '/' or is already taken by
|
|
673
|
+
another item in the case.
|
|
674
|
+
DataStoreException: If persisting the updated case fails.
|
|
675
|
+
"""
|
|
676
|
+
if not new_path or new_path.endswith("/"):
|
|
677
|
+
raise InvalidDataException("new_path must be a non-empty string and must not end with '/'")
|
|
678
|
+
|
|
679
|
+
ds = datastore()
|
|
680
|
+
|
|
681
|
+
_case = ds.case.get(key=case_id)
|
|
682
|
+
|
|
683
|
+
if not _case:
|
|
684
|
+
raise NotFoundException(f"Case {case_id} does not exist")
|
|
685
|
+
|
|
686
|
+
item = next((_item for _item in _case.items if _item["value"] == item_value), None)
|
|
687
|
+
if not item:
|
|
688
|
+
raise NotFoundException(f"Case item {item_value} does not exist in case {case_id}")
|
|
689
|
+
|
|
690
|
+
if any(_item["path"] == new_path for _item in _case.items if _item["value"] != item_value):
|
|
691
|
+
raise InvalidDataException(f"An item already exists at path '{new_path}' in case {case_id}")
|
|
692
|
+
|
|
693
|
+
item.path = new_path
|
|
694
|
+
|
|
695
|
+
if not ds.case.save(_case.case_id, _case):
|
|
696
|
+
raise DataStoreException("Failed to save case after item rename")
|
|
697
|
+
|
|
698
|
+
return _case
|
|
@@ -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.dev669"
|
|
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.dev657 → howler_api-4.0.0.dev669}/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
|
|
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.dev657 → howler_api-4.0.0.dev669}/howler/odm/models/ecs/autonomous_system.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
|
|
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.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/queues/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/queues/priority.py
RENAMED
|
File without changes
|
|
File without changes
|
{howler_api-4.0.0.dev657 → howler_api-4.0.0.dev669}/howler/remote/datatypes/user_quota_tracker.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
|