howler-api 4.0.0.dev652__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.
Files changed (208) hide show
  1. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/PKG-INFO +1 -1
  2. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v2/case.py +18 -16
  3. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/view.py +8 -0
  4. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/case_service.py +67 -57
  5. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/pyproject.toml +1 -1
  6. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/README.md +0 -0
  7. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/__init__.py +0 -0
  8. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/actions/__init__.py +0 -0
  9. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/actions/add_label.py +0 -0
  10. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/actions/change_field.py +0 -0
  11. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/actions/demote.py +0 -0
  12. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/actions/example_plugin.py +0 -0
  13. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/actions/prioritization.py +0 -0
  14. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/actions/promote.py +0 -0
  15. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/actions/remove_label.py +0 -0
  16. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/actions/transition.py +0 -0
  17. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/__init__.py +0 -0
  18. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/base.py +0 -0
  19. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/socket.py +0 -0
  20. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/__init__.py +0 -0
  21. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/action.py +0 -0
  22. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/analytic.py +0 -0
  23. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/auth.py +0 -0
  24. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/clue.py +0 -0
  25. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/configs.py +0 -0
  26. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/dossier.py +0 -0
  27. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/help.py +0 -0
  28. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/hit.py +0 -0
  29. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/notebook.py +0 -0
  30. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/overview.py +0 -0
  31. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/search.py +0 -0
  32. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/template.py +0 -0
  33. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/tool.py +0 -0
  34. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/user.py +0 -0
  35. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/utils/__init__.py +0 -0
  36. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/utils/etag.py +0 -0
  37. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v1/view.py +0 -0
  38. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v2/__init__.py +0 -0
  39. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v2/ingest.py +0 -0
  40. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/api/v2/search.py +0 -0
  41. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/app.py +0 -0
  42. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/README.md +0 -0
  43. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/__init__.py +0 -0
  44. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/classification.py +0 -0
  45. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/classification.yml +0 -0
  46. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/exceptions.py +0 -0
  47. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/loader.py +0 -0
  48. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/logging/__init__.py +0 -0
  49. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/logging/audit.py +0 -0
  50. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/logging/format.py +0 -0
  51. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/net.py +0 -0
  52. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/net_static.py +0 -0
  53. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/random_user.py +0 -0
  54. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/common/swagger.py +0 -0
  55. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/config.py +0 -0
  56. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/cronjobs/__init__.py +0 -0
  57. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/cronjobs/retention.py +0 -0
  58. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/cronjobs/view_cleanup.py +0 -0
  59. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/README.md +0 -0
  60. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/__init__.py +0 -0
  61. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/bulk.py +0 -0
  62. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/collection.py +0 -0
  63. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/constants.py +0 -0
  64. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/exceptions.py +0 -0
  65. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/howler_store.py +0 -0
  66. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/migrations/fix_process.py +0 -0
  67. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/operations.py +0 -0
  68. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/schemas.py +0 -0
  69. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/store.py +0 -0
  70. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/support/__init__.py +0 -0
  71. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/support/build.py +0 -0
  72. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/support/schemas.py +0 -0
  73. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/datastore/types.py +0 -0
  74. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/error.py +0 -0
  75. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/external/__init__.py +0 -0
  76. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/external/generate_mitre.py +0 -0
  77. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/external/generate_sigma_rules.py +0 -0
  78. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/external/generate_tlds.py +0 -0
  79. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/external/reindex_data.py +0 -0
  80. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/external/wipe_databases.py +0 -0
  81. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/gunicorn_config.py +0 -0
  82. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/healthz.py +0 -0
  83. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/helper/__init__.py +0 -0
  84. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/helper/azure.py +0 -0
  85. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/helper/discover.py +0 -0
  86. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/helper/hit.py +0 -0
  87. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/helper/oauth.py +0 -0
  88. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/helper/search.py +0 -0
  89. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/helper/workflow.py +0 -0
  90. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/helper/ws.py +0 -0
  91. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/README.md +0 -0
  92. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/__init__.py +0 -0
  93. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/base.py +0 -0
  94. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/charter.txt +0 -0
  95. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/constants.py +0 -0
  96. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/helper.py +0 -0
  97. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/howler_enum.py +0 -0
  98. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/__init__.py +0 -0
  99. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/action.py +0 -0
  100. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/analytic.py +0 -0
  101. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/assemblyline.py +0 -0
  102. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/aws.py +0 -0
  103. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/azure.py +0 -0
  104. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/case.py +0 -0
  105. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/cbs.py +0 -0
  106. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/clue.py +0 -0
  107. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/config.py +0 -0
  108. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/dossier.py +0 -0
  109. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/__init__.py +0 -0
  110. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/agent.py +0 -0
  111. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/autonomous_system.py +0 -0
  112. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/client.py +0 -0
  113. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/cloud.py +0 -0
  114. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/code_signature.py +0 -0
  115. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/container.py +0 -0
  116. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/dns.py +0 -0
  117. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/egress.py +0 -0
  118. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/elf.py +0 -0
  119. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/email.py +0 -0
  120. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/error.py +0 -0
  121. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/event.py +0 -0
  122. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/faas.py +0 -0
  123. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/file.py +0 -0
  124. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/geo.py +0 -0
  125. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/group.py +0 -0
  126. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/hash.py +0 -0
  127. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/host.py +0 -0
  128. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/http.py +0 -0
  129. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/ingress.py +0 -0
  130. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/interface.py +0 -0
  131. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/network.py +0 -0
  132. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/observer.py +0 -0
  133. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/organization.py +0 -0
  134. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/os.py +0 -0
  135. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/pe.py +0 -0
  136. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/process.py +0 -0
  137. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/registry.py +0 -0
  138. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/related.py +0 -0
  139. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/rule.py +0 -0
  140. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/server.py +0 -0
  141. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/threat.py +0 -0
  142. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/tls.py +0 -0
  143. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/url.py +0 -0
  144. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/user.py +0 -0
  145. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/user_agent.py +0 -0
  146. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/ecs/vulnerability.py +0 -0
  147. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/gcp.py +0 -0
  148. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/hit.py +0 -0
  149. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/howler_data.py +0 -0
  150. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/lead.py +0 -0
  151. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/localized_label.py +0 -0
  152. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/observable.py +0 -0
  153. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/overview.py +0 -0
  154. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/pivot.py +0 -0
  155. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/record.py +0 -0
  156. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/template.py +0 -0
  157. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/models/user.py +0 -0
  158. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/random_data.py +0 -0
  159. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/odm/randomizer.py +0 -0
  160. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/patched.py +0 -0
  161. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/plugins/__init__.py +0 -0
  162. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/plugins/config.py +0 -0
  163. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/__init__.py +0 -0
  164. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/README.md +0 -0
  165. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/__init__.py +0 -0
  166. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/counters.py +0 -0
  167. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/events.py +0 -0
  168. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/hash.py +0 -0
  169. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/lock.py +0 -0
  170. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/__init__.py +0 -0
  171. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/comms.py +0 -0
  172. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/multi.py +0 -0
  173. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/named.py +0 -0
  174. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/queues/priority.py +0 -0
  175. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/set.py +0 -0
  176. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/remote/datatypes/user_quota_tracker.py +0 -0
  177. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/security/__init__.py +0 -0
  178. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/security/socket.py +0 -0
  179. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/security/utils.py +0 -0
  180. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/__init__.py +0 -0
  181. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/action_service.py +0 -0
  182. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/analytic_service.py +0 -0
  183. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/auth_service.py +0 -0
  184. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/config_service.py +0 -0
  185. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/docs_service.py +0 -0
  186. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/dossier_service.py +0 -0
  187. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/event_service.py +0 -0
  188. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/hit_service.py +0 -0
  189. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/jwt_service.py +0 -0
  190. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/lucene_service.py +0 -0
  191. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/notebook_service.py +0 -0
  192. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/observable_service.py +0 -0
  193. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/overview_service.py +0 -0
  194. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/search_service.py +0 -0
  195. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/template_service.py +0 -0
  196. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/services/user_service.py +0 -0
  197. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/__init__.py +0 -0
  198. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/annotations.py +0 -0
  199. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/chunk.py +0 -0
  200. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/compat.py +0 -0
  201. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/dict_utils.py +0 -0
  202. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/isotime.py +0 -0
  203. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/list_utils.py +0 -0
  204. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/lucene.py +0 -0
  205. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/path.py +0 -0
  206. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/socket_utils.py +0 -0
  207. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/str_utils.py +0 -0
  208. {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev657}/howler/utils/uid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howler-api
3
- Version: 4.0.0.dev652
3
+ Version: 4.0.0.dev657
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -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
- if "value" not in body:
234
- return bad_request(err="Case 'value' is required")
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/<value>", methods=["DELETE"])
249
+ @case_api.route("/<id>/items", methods=["DELETE"])
254
250
  @api_login(required_priv=["R", "W"])
255
- def delete_item(id: str, value: str, **kwargs):
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
- None
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: Optional path prefix for organizing the item within the case.
255
- A trailing "/" is appended automatically if not present.
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
- case = ds.case.get(key=case_id)
313
+ _case = ds.case.get(case_id)
310
314
 
311
- if case is None:
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 case.items):
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(key=item.value)
321
+ hit = ds.hit.get(item.value)
318
322
 
319
- if hit is None:
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
- case.items.append(item)
325
+ _add_backreference(hit, _case.case_id)
326
326
 
327
- if not datastore().case.save(case.case_id, case):
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
- _add_backreference(hit, case.case_id)
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. Sets the item's path to include the
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
- def append_case(case_id: str, item: CaseItem):
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. Sets the
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(key=case_id)
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 a reference item to a case.
443
+ def append_reference(case_id: str, item: CaseItem) -> Case:
444
+ """Append an external reference item to a case.
453
445
 
454
- Not yet implemented.
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 representing the external reference to append.
451
+ item: A CaseItem whose ``value`` is the external URL to reference.
459
452
 
460
453
  Raises:
461
- NotImplementedError: Always raised; this feature is not yet implemented.
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
- raise NotImplementedError
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
- case_item = next((item for item in _case.items if item["value"] == item_value), None)
601
- if not case_item:
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 case_item.type in [CaseItemTypes.HIT, CaseItemTypes.OBSERVABLE]:
606
- backing_obj = ds[case_item.type].get(case_item.value)
615
+ if item.type in [CaseItemTypes.HIT, CaseItemTypes.OBSERVABLE]:
616
+ backing_obj = ds[item.type].get(item.value)
607
617
 
608
- _case.items.remove(case_item)
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.dev652"
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>",