howler-api 4.1.0.dev1560__tar.gz → 4.1.0.dev1573__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.
Files changed (228) hide show
  1. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/PKG-INFO +1 -1
  2. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/add_to_bundle.py +8 -2
  3. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/add_to_case.py +28 -7
  4. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/socket.py +39 -5
  5. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/hit.py +160 -103
  6. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/search.py +20 -31
  7. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/tool.py +3 -4
  8. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/user.py +5 -6
  9. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/utils/etag.py +30 -17
  10. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v2/case.py +98 -43
  11. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v2/ingest.py +68 -20
  12. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/classification.py +4 -1
  13. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/collection.py +142 -93
  14. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/security/socket.py +4 -4
  15. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/security/utils.py +94 -3
  16. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/bundle_compat_service.py +38 -30
  17. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/case_service.py +238 -148
  18. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/correlation_service.py +19 -23
  19. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/event_service.py +81 -2
  20. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/hit_service.py +52 -6
  21. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/user_service.py +5 -0
  22. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/socket_utils.py +14 -5
  23. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/pyproject.toml +1 -1
  24. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/README.md +0 -0
  25. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/__init__.py +0 -0
  26. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/__init__.py +0 -0
  27. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/add_label.py +0 -0
  28. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/change_field.py +0 -0
  29. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/demote.py +0 -0
  30. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/example_plugin.py +0 -0
  31. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/prioritization.py +0 -0
  32. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/promote.py +0 -0
  33. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/remove_from_bundle.py +0 -0
  34. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/remove_label.py +0 -0
  35. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/actions/transition.py +0 -0
  36. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/__init__.py +0 -0
  37. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/base.py +0 -0
  38. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/__init__.py +0 -0
  39. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/action.py +0 -0
  40. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/analytic.py +0 -0
  41. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/auth.py +0 -0
  42. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/clue.py +0 -0
  43. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/configs.py +0 -0
  44. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/dossier.py +0 -0
  45. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/help.py +0 -0
  46. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/notebook.py +0 -0
  47. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/overview.py +0 -0
  48. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/template.py +0 -0
  49. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/utils/__init__.py +0 -0
  50. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/utils/params.py +0 -0
  51. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v1/view.py +0 -0
  52. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v2/__init__.py +0 -0
  53. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v2/fuzzy.py +0 -0
  54. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/api/v2/search.py +0 -0
  55. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/app.py +0 -0
  56. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/README.md +0 -0
  57. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/__init__.py +0 -0
  58. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/classification.yml +0 -0
  59. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/exceptions.py +0 -0
  60. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/loader.py +0 -0
  61. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/logging/__init__.py +0 -0
  62. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/logging/audit.py +0 -0
  63. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/logging/format.py +0 -0
  64. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/net.py +0 -0
  65. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/net_static.py +0 -0
  66. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/random_user.py +0 -0
  67. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/common/swagger.py +0 -0
  68. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/config.py +0 -0
  69. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/cronjobs/__init__.py +0 -0
  70. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/cronjobs/action_queue_worker.py +0 -0
  71. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/cronjobs/correlation.py +0 -0
  72. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/cronjobs/retention.py +0 -0
  73. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/cronjobs/view_cleanup.py +0 -0
  74. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/README.md +0 -0
  75. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/__init__.py +0 -0
  76. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/bulk.py +0 -0
  77. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/constants.py +0 -0
  78. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/exceptions.py +0 -0
  79. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/howler_store.py +0 -0
  80. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/migrations/fix_process.py +0 -0
  81. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/operations.py +0 -0
  82. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/schemas.py +0 -0
  83. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/store.py +0 -0
  84. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/support/__init__.py +0 -0
  85. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/support/build.py +0 -0
  86. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/support/schemas.py +0 -0
  87. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/types.py +0 -0
  88. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/datastore/utils.py +0 -0
  89. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/error.py +0 -0
  90. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/external/README.md +0 -0
  91. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/external/__init__.py +0 -0
  92. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/external/check_duplicates.py +0 -0
  93. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/external/generate_mitre.py +0 -0
  94. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/external/generate_sigma_rules.py +0 -0
  95. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/external/generate_tlds.py +0 -0
  96. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/external/reindex_data.py +0 -0
  97. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/external/wipe_databases.py +0 -0
  98. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/gunicorn_config.py +0 -0
  99. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/healthz.py +0 -0
  100. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/helper/__init__.py +0 -0
  101. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/helper/azure.py +0 -0
  102. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/helper/discover.py +0 -0
  103. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/helper/hit.py +0 -0
  104. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/helper/oauth.py +0 -0
  105. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/helper/search.py +0 -0
  106. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/helper/workflow.py +0 -0
  107. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/helper/ws.py +0 -0
  108. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/README.md +0 -0
  109. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/__init__.py +0 -0
  110. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/base.py +0 -0
  111. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/charter.txt +0 -0
  112. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/constants.py +0 -0
  113. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/helper.py +0 -0
  114. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/howler_enum.py +0 -0
  115. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/mixins.py +0 -0
  116. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/__init__.py +0 -0
  117. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/action.py +0 -0
  118. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/analytic.py +0 -0
  119. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/assemblyline.py +0 -0
  120. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/aws.py +0 -0
  121. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/azure.py +0 -0
  122. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/case.py +0 -0
  123. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/cbs.py +0 -0
  124. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/clue.py +0 -0
  125. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/config.py +0 -0
  126. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/dossier.py +0 -0
  127. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/__init__.py +0 -0
  128. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/agent.py +0 -0
  129. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/autonomous_system.py +0 -0
  130. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/client.py +0 -0
  131. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/cloud.py +0 -0
  132. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/code_signature.py +0 -0
  133. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/container.py +0 -0
  134. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/dns.py +0 -0
  135. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/egress.py +0 -0
  136. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/elf.py +0 -0
  137. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/email.py +0 -0
  138. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/error.py +0 -0
  139. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/event.py +0 -0
  140. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/faas.py +0 -0
  141. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/file.py +0 -0
  142. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/geo.py +0 -0
  143. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/group.py +0 -0
  144. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/hash.py +0 -0
  145. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/host.py +0 -0
  146. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/http.py +0 -0
  147. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/ingress.py +0 -0
  148. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/interface.py +0 -0
  149. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/network.py +0 -0
  150. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/observer.py +0 -0
  151. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/organization.py +0 -0
  152. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/os.py +0 -0
  153. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/pe.py +0 -0
  154. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/process.py +0 -0
  155. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/registry.py +0 -0
  156. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/related.py +0 -0
  157. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/rule.py +0 -0
  158. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/server.py +0 -0
  159. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/threat.py +0 -0
  160. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/tls.py +0 -0
  161. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/url.py +0 -0
  162. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/user.py +0 -0
  163. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/user_agent.py +0 -0
  164. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/ecs/vulnerability.py +0 -0
  165. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/event.py +0 -0
  166. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/gcp.py +0 -0
  167. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/hit.py +0 -0
  168. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/howler_data.py +0 -0
  169. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/lead.py +0 -0
  170. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/localized_label.py +0 -0
  171. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/overview.py +0 -0
  172. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/pivot.py +0 -0
  173. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/record.py +0 -0
  174. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/template.py +0 -0
  175. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/user.py +0 -0
  176. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/models/view.py +0 -0
  177. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/random_data.py +0 -0
  178. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/odm/randomizer.py +0 -0
  179. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/patched.py +0 -0
  180. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/plugins/__init__.py +0 -0
  181. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/plugins/config.py +0 -0
  182. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/__init__.py +0 -0
  183. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/README.md +0 -0
  184. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/__init__.py +0 -0
  185. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/counters.py +0 -0
  186. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/events.py +0 -0
  187. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/hash.py +0 -0
  188. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/lock.py +0 -0
  189. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/queues/__init__.py +0 -0
  190. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/queues/comms.py +0 -0
  191. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/queues/multi.py +0 -0
  192. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/queues/named.py +0 -0
  193. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/queues/priority.py +0 -0
  194. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/set.py +0 -0
  195. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/remote/datatypes/user_quota_tracker.py +0 -0
  196. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/security/__init__.py +0 -0
  197. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/security/login.py +0 -0
  198. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/__init__.py +0 -0
  199. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/action_service.py +0 -0
  200. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/analytic_service.py +0 -0
  201. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/auth_service.py +0 -0
  202. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/comms_service.py +0 -0
  203. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/config_service.py +0 -0
  204. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/docs_service.py +0 -0
  205. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/dossier_service.py +0 -0
  206. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/fuzzy_service.py +0 -0
  207. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/jwt_service.py +0 -0
  208. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/lucene_service.py +0 -0
  209. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/notebook_service.py +0 -0
  210. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/overview_service.py +0 -0
  211. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/search_service.py +0 -0
  212. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/template_service.py +0 -0
  213. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/services/viewer_service.py +0 -0
  214. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/telemetry.py +0 -0
  215. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/__init__.py +0 -0
  216. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/annotations.py +0 -0
  217. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/chunk.py +0 -0
  218. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/compat.py +0 -0
  219. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/constants.py +0 -0
  220. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/dict_utils.py +0 -0
  221. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/indexes.py +0 -0
  222. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/isotime.py +0 -0
  223. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/list_utils.py +0 -0
  224. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/lucene.py +0 -0
  225. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/net_utils.py +0 -0
  226. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/path.py +0 -0
  227. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/str_utils.py +0 -0
  228. {howler_api-4.1.0.dev1560 → howler_api-4.1.0.dev1573}/howler/utils/uid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howler-api
3
- Version: 4.1.0.dev1560
3
+ Version: 4.1.0.dev1573
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -8,7 +8,7 @@ from howler.common.loader import datastore
8
8
  from howler.odm.models.action import VALID_TRIGGERS
9
9
  from howler.odm.models.case import CaseItemTypes
10
10
  from howler.odm.models.user import User
11
- from howler.services import bundle_compat_service, case_service
11
+ from howler.services import bundle_compat_service, case_service, comms_service
12
12
  from howler.utils.str_utils import sanitize_lucene_query
13
13
 
14
14
  OPERATION_ID = "add_to_bundle"
@@ -70,7 +70,8 @@ def execute(query: str, bundle_id: Optional[str] = None, user: Optional[User] =
70
70
  )
71
71
  return report
72
72
 
73
- folder = case_service.get_parent_from_path(case, "hits", create_if_missing=True)
73
+ original_item_count = len(case.items)
74
+ folder = case_service.get_parent_from_path(case, "hits", create_if_missing=True, user=user)
74
75
 
75
76
  added = []
76
77
  skipped = []
@@ -83,11 +84,16 @@ def execute(query: str, bundle_id: Optional[str] = None, user: Optional[User] =
83
84
  item_name=name,
84
85
  item_value=hit.howler.id,
85
86
  item_parent=folder.id if folder else None,
87
+ user=user,
86
88
  )
87
89
  added.append(hit.howler.id)
88
90
  except Exception: # pragma: no cover
89
91
  skipped.append(hit.howler.id)
90
92
 
93
+ if len(case.items) != original_item_count:
94
+ case.save(refresh="wait_for")
95
+ comms_service.emit("cases", {"case": case.as_primitives()})
96
+
91
97
  if skipped:
92
98
  report.append(
93
99
  {
@@ -1,19 +1,23 @@
1
- from typing import Optional
2
-
3
1
  import chevron
4
2
 
5
3
  from howler.common.exceptions import InvalidDataException, NotFoundException
6
4
  from howler.common.loader import datastore
5
+ from howler.common.logging import get_logger
6
+ from howler.datastore.exceptions import DataStoreException, VersionConflictException
7
7
  from howler.odm.models.action import VALID_TRIGGERS
8
- from howler.services import case_service
8
+ from howler.odm.models.user import User
9
+ from howler.services import case_service, comms_service
10
+
11
+ logger = get_logger(__file__)
9
12
 
10
13
  OPERATION_ID = "add_to_case"
11
14
 
12
15
 
13
16
  def execute( # noqa: C901
14
17
  query: str,
15
- case_id: Optional[str] = None,
18
+ case_id: str | None = None,
16
19
  destination: str = "related/{{howler.analytic}} ({{howler.id}})",
20
+ user: User | None = None,
17
21
  **kwargs,
18
22
  ):
19
23
  """Add matching alerts to a given case.
@@ -37,8 +41,8 @@ def execute( # noqa: C901
37
41
 
38
42
  ds = datastore()
39
43
 
40
- case = ds.case.get(case_id)
41
- if case is None:
44
+ case, version = case_service.get_case(case_id, as_odm=True, version=True, user=user)
45
+ if not case:
42
46
  return [
43
47
  {
44
48
  "query": query,
@@ -63,6 +67,7 @@ def execute( # noqa: C901
63
67
  report = []
64
68
  skipped = []
65
69
  added = []
70
+ original_item_count = len(case.items)
66
71
 
67
72
  for hit in hits:
68
73
  rendered_destination = chevron.render(destination, hit.as_primitives())
@@ -73,7 +78,7 @@ def execute( # noqa: C901
73
78
  name = rendered_destination
74
79
 
75
80
  try:
76
- parent = case_service.get_parent_from_path(case, item_path, create_if_missing=True)
81
+ parent = case_service.get_parent_from_path(case, item_path, create_if_missing=True, user=user)
77
82
 
78
83
  case_service.append_case_item(
79
84
  case,
@@ -81,6 +86,7 @@ def execute( # noqa: C901
81
86
  item_value=hit.howler.id,
82
87
  item_name=name,
83
88
  item_parent=parent.id if parent else None,
89
+ user=user,
84
90
  )
85
91
  added.append(hit.howler.id)
86
92
  except InvalidDataException as e:
@@ -90,6 +96,21 @@ def execute( # noqa: C901
90
96
  except Exception as e: # pragma: no cover
91
97
  skipped.append(f"{hit.howler.id}: {e}")
92
98
 
99
+ if len(case.items) != original_item_count:
100
+ try:
101
+ case.save(refresh="wait_for", version=version)
102
+ except (DataStoreException, VersionConflictException):
103
+ logger.exception("Exception on save:")
104
+ return [
105
+ {
106
+ "query": query,
107
+ "outcome": "error",
108
+ "title": "Case update failed",
109
+ "message": "There was a datastore error or version conflict when updating the case.",
110
+ }
111
+ ]
112
+ comms_service.emit("cases", {"case": case.as_primitives()})
113
+
93
114
  if added:
94
115
  report.append(
95
116
  {
@@ -1,3 +1,5 @@
1
+ """Websocket and inter-pod communication API endpoints."""
2
+
1
3
  import base64
2
4
  import json
3
5
  import os
@@ -11,8 +13,11 @@ import howler.services.viewer_service as viewer_service
11
13
  from howler.api import ok, unauthorized
12
14
  from howler.common.logging import get_logger
13
15
  from howler.helper.ws import ConnectionClosed, Server
16
+ from howler.odm.models.user import User
14
17
  from howler.security.login import api_login
15
18
  from howler.security.socket import websocket_auth, ws_response
19
+ from howler.security.utils import is_classification_accessible
20
+ from howler.services import case_service
16
21
  from howler.utils.socket_utils import check_action
17
22
 
18
23
  HWL_INTERPOD_COMMS_SECRET = os.getenv("HWL_INTERPOD_COMMS_SECRET", "secret")
@@ -70,7 +75,7 @@ def get_viewers(entity_id: str, **kwargs): # pragma: no cover
70
75
  @tracer.start_as_current_span(f"{__name__}.connect")
71
76
  @socket_api.route("/connect", websocket=True) # type: ignore
72
77
  @websocket_auth(required_priv=["R"])
73
- def connect(ws: Server, *args: Any, ws_id: str, **kwargs): # noqa: C901
78
+ def connect(ws: Server, *args: Any, ws_id: str, user: User | None = None, **kwargs): # noqa: C901
74
79
  """Connect to the server to monitor for updates via websocket
75
80
 
76
81
  Variables:
@@ -86,22 +91,39 @@ def connect(ws: Server, *args: Any, ws_id: str, **kwargs): # noqa: C901
86
91
  outstanding_actions: list[tuple[str, str, bool]] = []
87
92
 
88
93
  def send_hit(data: dict[str, Any]):
94
+ """Send an access-controlled hit update to the websocket client."""
95
+ if user and user.classification:
96
+ if not is_classification_accessible(user, data["hit"].get("classification")):
97
+ return
98
+
89
99
  logger.debug("Sending hit update: %s", data["hit"]["howler"]["id"])
90
100
  ws.send(ws_response("hits", data))
91
101
 
92
102
  def send_broadcast(data: dict[str, str]):
103
+ """Send a broadcast event to the websocket client."""
93
104
  logger.debug("Sending broadcast: %s", data)
94
105
  ws.send(ws_response("broadcast", {"event": data}))
95
106
 
96
107
  def send_action(data: dict[str, str]):
108
+ """Send an action update to the websocket client."""
97
109
  logger.debug("Sending action: %s", data)
98
110
  ws.send(ws_response("action", data))
99
111
 
100
112
  def send_case(data: dict[str, Any]): # pragma: no cover
101
- logger.debug("Sending case update: %s", data.get("case", {}).get("case_id", "unknown"))
102
- ws.send(ws_response("cases", data))
113
+ """Send an access-controlled case update to the websocket client."""
114
+ case_data = data.get("case", {})
115
+
116
+ if user and user.classification:
117
+ if not is_classification_accessible(user, case_data.get("classification")):
118
+ return
119
+
120
+ case_service.filter_case_items_by_classification(case_data, user.classification)
121
+
122
+ logger.debug("Sending case update: %s", case_data.get("case_id"))
123
+ ws.send(ws_response("cases", {**data, "case": case_data}))
103
124
 
104
125
  def send_viewers_update(data: dict[str, Any]): # pragma: no cover
126
+ """Send a viewer-presence update to the websocket client."""
105
127
  logger.debug("Sending viewers update: %s", data.get("id", "unknown"))
106
128
  ws.send(ws_response("viewers_update", data))
107
129
 
@@ -135,7 +157,12 @@ def connect(ws: Server, *args: Any, ws_id: str, **kwargs): # noqa: C901
135
157
  return
136
158
 
137
159
  outstanding_actions = check_action(
138
- obj["id"], obj["action"], obj["broadcast"], outstanding_actions=outstanding_actions, **kwargs
160
+ obj["id"],
161
+ obj["action"],
162
+ obj["broadcast"],
163
+ outstanding_actions=outstanding_actions,
164
+ user=user,
165
+ **kwargs,
139
166
  )
140
167
  logger.debug("%s: Outstanding actions count=%s", ws_id, len(outstanding_actions))
141
168
  else:
@@ -154,5 +181,12 @@ def connect(ws: Server, *args: Any, ws_id: str, **kwargs): # noqa: C901
154
181
  comms_service.off("viewers_update", send_viewers_update)
155
182
 
156
183
  for id, action, broadcast in outstanding_actions:
157
- outstanding_actions = check_action(id, action, broadcast, outstanding_actions=outstanding_actions, **kwargs)
184
+ outstanding_actions = check_action(
185
+ id,
186
+ action,
187
+ broadcast,
188
+ outstanding_actions=outstanding_actions,
189
+ user=user,
190
+ **kwargs,
191
+ )
158
192
  logger.info("%s: WS connect handler finished", ws_id)