howler-api 2.10.0.dev65__tar.gz → 2.10.0.dev80__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 (194) hide show
  1. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/PKG-INFO +1 -1
  2. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/__init__.py +4 -1
  3. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/borealis.py +1 -1
  4. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/app.py +7 -1
  5. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/collection.py +46 -25
  6. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/howler_store.py +4 -4
  7. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/notebook_service.py +2 -6
  8. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/pyproject.toml +1 -1
  9. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/README.md +0 -0
  10. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/__init__.py +0 -0
  11. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/add_label.py +0 -0
  12. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/add_to_bundle.py +0 -0
  13. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/change_field.py +0 -0
  14. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/demote.py +0 -0
  15. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/example_plugin.py +0 -0
  16. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/prioritization.py +0 -0
  17. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/promote.py +0 -0
  18. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/remove_from_bundle.py +0 -0
  19. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/remove_label.py +0 -0
  20. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/actions/transition.py +0 -0
  21. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/__init__.py +0 -0
  22. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/base.py +0 -0
  23. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/socket.py +0 -0
  24. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/__init__.py +0 -0
  25. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/action.py +0 -0
  26. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/analytic.py +0 -0
  27. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/auth.py +0 -0
  28. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/configs.py +0 -0
  29. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/dossier.py +0 -0
  30. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/help.py +0 -0
  31. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/hit.py +0 -0
  32. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/notebook.py +0 -0
  33. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/overview.py +0 -0
  34. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/search.py +0 -0
  35. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/template.py +0 -0
  36. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/tool.py +0 -0
  37. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/user.py +0 -0
  38. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/utils/__init__.py +0 -0
  39. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/utils/etag.py +0 -0
  40. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/api/v1/view.py +0 -0
  41. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/README.md +0 -0
  42. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/__init__.py +0 -0
  43. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/classification.py +0 -0
  44. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/classification.yml +0 -0
  45. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/exceptions.py +0 -0
  46. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/hexdump.py +0 -0
  47. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/iprange.py +0 -0
  48. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/loader.py +0 -0
  49. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/logging/__init__.py +0 -0
  50. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/logging/audit.py +0 -0
  51. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/logging/format.py +0 -0
  52. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/net.py +0 -0
  53. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/net_static.py +0 -0
  54. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/random_user.py +0 -0
  55. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/common/swagger.py +0 -0
  56. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/config.py +0 -0
  57. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/cronjobs/__init__.py +0 -0
  58. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/cronjobs/retention.py +0 -0
  59. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/cronjobs/rules.py +0 -0
  60. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/README.md +0 -0
  61. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/__init__.py +0 -0
  62. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/bulk.py +0 -0
  63. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/constants.py +0 -0
  64. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/exceptions.py +0 -0
  65. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/migrations/fix_process.py +0 -0
  66. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/operations.py +0 -0
  67. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/schemas.py +0 -0
  68. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/store.py +0 -0
  69. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/support/__init__.py +0 -0
  70. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/support/build.py +0 -0
  71. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/support/schemas.py +0 -0
  72. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/datastore/types.py +0 -0
  73. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/error.py +0 -0
  74. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/external/__init__.py +0 -0
  75. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/external/generate_mitre.py +0 -0
  76. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/external/generate_sigma_rules.py +0 -0
  77. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/external/generate_tlds.py +0 -0
  78. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/external/reindex_data.py +0 -0
  79. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/external/wipe_databases.py +0 -0
  80. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/gunicorn_config.py +0 -0
  81. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/healthz.py +0 -0
  82. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/helper/__init__.py +0 -0
  83. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/helper/azure.py +0 -0
  84. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/helper/discover.py +0 -0
  85. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/helper/hit.py +0 -0
  86. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/helper/oauth.py +0 -0
  87. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/helper/search.py +0 -0
  88. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/helper/workflow.py +0 -0
  89. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/helper/ws.py +0 -0
  90. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/README.md +0 -0
  91. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/__init__.py +0 -0
  92. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/base.py +0 -0
  93. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/charter.txt +0 -0
  94. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/helper.py +0 -0
  95. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/howler_enum.py +0 -0
  96. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/__init__.py +0 -0
  97. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/action.py +0 -0
  98. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/analytic.py +0 -0
  99. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/assemblyline.py +0 -0
  100. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/aws.py +0 -0
  101. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/azure.py +0 -0
  102. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/cbs.py +0 -0
  103. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/config.py +0 -0
  104. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/dossier.py +0 -0
  105. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/__init__.py +0 -0
  106. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/agent.py +0 -0
  107. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/autonomous_system.py +0 -0
  108. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/client.py +0 -0
  109. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/cloud.py +0 -0
  110. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/code_signature.py +0 -0
  111. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/container.py +0 -0
  112. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/dns.py +0 -0
  113. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/egress.py +0 -0
  114. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/elf.py +0 -0
  115. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/email.py +0 -0
  116. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/error.py +0 -0
  117. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/event.py +0 -0
  118. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/faas.py +0 -0
  119. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/file.py +0 -0
  120. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/geo.py +0 -0
  121. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/group.py +0 -0
  122. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/hash.py +0 -0
  123. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/host.py +0 -0
  124. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/http.py +0 -0
  125. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/ingress.py +0 -0
  126. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/interface.py +0 -0
  127. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/network.py +0 -0
  128. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/observer.py +0 -0
  129. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/organization.py +0 -0
  130. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/os.py +0 -0
  131. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/pe.py +0 -0
  132. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/process.py +0 -0
  133. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/registry.py +0 -0
  134. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/related.py +0 -0
  135. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/rule.py +0 -0
  136. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/server.py +0 -0
  137. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/threat.py +0 -0
  138. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/tls.py +0 -0
  139. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/url.py +0 -0
  140. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/user.py +0 -0
  141. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/user_agent.py +0 -0
  142. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/ecs/vulnerability.py +0 -0
  143. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/gcp.py +0 -0
  144. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/hit.py +0 -0
  145. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/howler_data.py +0 -0
  146. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/lead.py +0 -0
  147. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/localized_label.py +0 -0
  148. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/overview.py +0 -0
  149. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/pivot.py +0 -0
  150. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/template.py +0 -0
  151. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/user.py +0 -0
  152. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/models/view.py +0 -0
  153. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/random_data.py +0 -0
  154. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/odm/randomizer.py +0 -0
  155. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/patched.py +0 -0
  156. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/__init__.py +0 -0
  157. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/README.md +0 -0
  158. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/__init__.py +0 -0
  159. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/counters.py +0 -0
  160. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/events.py +0 -0
  161. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/hash.py +0 -0
  162. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/lock.py +0 -0
  163. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/queues/__init__.py +0 -0
  164. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/queues/comms.py +0 -0
  165. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/queues/multi.py +0 -0
  166. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/queues/named.py +0 -0
  167. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/queues/priority.py +0 -0
  168. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/set.py +0 -0
  169. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/remote/datatypes/user_quota_tracker.py +0 -0
  170. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/security/__init__.py +0 -0
  171. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/security/socket.py +0 -0
  172. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/security/utils.py +0 -0
  173. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/__init__.py +0 -0
  174. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/action_service.py +0 -0
  175. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/analytic_service.py +0 -0
  176. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/auth_service.py +0 -0
  177. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/config_service.py +0 -0
  178. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/dossier_service.py +0 -0
  179. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/event_service.py +0 -0
  180. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/hit_service.py +0 -0
  181. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/jwt_service.py +0 -0
  182. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/lucene_service.py +0 -0
  183. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/services/user_service.py +0 -0
  184. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/__init__.py +0 -0
  185. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/annotations.py +0 -0
  186. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/chunk.py +0 -0
  187. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/dict_utils.py +0 -0
  188. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/isotime.py +0 -0
  189. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/list_utils.py +0 -0
  190. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/lucene.py +0 -0
  191. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/path.py +0 -0
  192. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/socket_utils.py +0 -0
  193. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/str_utils.py +0 -0
  194. {howler_api-2.10.0.dev65 → howler_api-2.10.0.dev80}/howler/utils/uid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: howler-api
3
- Version: 2.10.0.dev65
3
+ Version: 2.10.0.dev80
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -30,7 +30,7 @@ def __sanitize_specification(spec: dict[str, Any]) -> dict[str, Any]:
30
30
  .replace("\n ", "\n")
31
31
  .replace("Args:", "Args:\n"),
32
32
  },
33
- "steps": [{**step, "args": {k: list(v) for k, v in step["args"].items()}} for step in spec["steps"]],
33
+ "steps": [{**step, "args": {k: list(v) for k, v in step["args"].items()}} for step in spec.get("steps", [])],
34
34
  }
35
35
 
36
36
 
@@ -146,6 +146,9 @@ def specifications() -> list[dict[str, Any]]:
146
146
  try:
147
147
  automation = importlib.import_module(f"{module_name}.actions.{module.stem}")
148
148
 
149
+ if module_name != "howler":
150
+ logger.info("Enabling action %s from plugin %s", automation.specification()["id"], module_name)
151
+
149
152
  specifications.append(__sanitize_specification(automation.specification()))
150
153
 
151
154
  except Exception: # pragma: no cover
@@ -32,7 +32,7 @@ def get_token(access_token: str) -> str:
32
32
  get_borealis_token = module.get_borealis_token
33
33
  break
34
34
  except ImportError:
35
- logger.exception("Plugin %s does not modify the borealis access token.")
35
+ logger.info("Plugin %s does not modify the borealis access token.")
36
36
 
37
37
  if get_borealis_token:
38
38
  borealis_access_token = get_borealis_token(access_token)
@@ -4,12 +4,18 @@ from pathlib import Path
4
4
 
5
5
  from dotenv import load_dotenv
6
6
 
7
+ from howler.odm.models.config import config
8
+
7
9
  load_dotenv()
8
10
 
9
11
  # We append the plugin directory for howler to the python part
10
12
  PLUGIN_PATH = Path(os.environ.get("HWL_PLUGIN_DIRECTORY", "/etc/howler/plugins"))
11
13
  sys.path.insert(0, str(PLUGIN_PATH))
12
- sys.path.append(str(PLUGIN_PATH / f".venv/lib/python3.{sys.version_info.minor}/site-packages"))
14
+ if config.ui.debug and PLUGIN_PATH.exists():
15
+ for _plugin in PLUGIN_PATH.iterdir():
16
+ sys.path.append(
17
+ str(Path(os.path.realpath(_plugin)) / f"../.venv/lib/python3.{sys.version_info.minor}/site-packages")
18
+ )
13
19
 
14
20
  import importlib
15
21
  import logging
@@ -57,7 +57,7 @@ if typing.TYPE_CHECKING:
57
57
 
58
58
  TRANSPORT_TIMEOUT = int(environ.get("HWL_DATASTORE_TRANSPORT_TIMEOUT", "10"))
59
59
 
60
- log = logging.getLogger("howler.api.datastore")
60
+ logger = logging.getLogger("howler.api.datastore")
61
61
  ModelType = TypeVar("ModelType", bound=Model)
62
62
  write_block_settings = {"settings": {"index.blocks.write": True}}
63
63
  write_unblock_settings = {"settings": {"index.blocks.write": None}}
@@ -219,7 +219,7 @@ class ESCollection(Generic[ModelType]):
219
219
  if not ESCollection.IGNORE_ENSURE_COLLECTION:
220
220
  self._ensure_collection()
221
221
  else:
222
- log.warning("Skipping ensure collection! This is dangerous. Waiting five seconds before continuing.")
222
+ logger.warning("Skipping ensure collection! This is dangerous. Waiting five seconds before continuing.")
223
223
  time.sleep(5)
224
224
 
225
225
  self.stored_fields = {}
@@ -297,7 +297,7 @@ class ESCollection(Generic[ModelType]):
297
297
  ignore=(404,),
298
298
  )
299
299
  if not resp.get("succeeded", False):
300
- log.warning(
300
+ logger.warning(
301
301
  f"Could not clear scroll ID {scroll_id}, there is potential "
302
302
  "memory leak in you Elastic cluster..."
303
303
  )
@@ -319,7 +319,7 @@ class ESCollection(Generic[ModelType]):
319
319
  ret_val = func(*args, **kwargs)
320
320
 
321
321
  if retries:
322
- log.info("Reconnected to elasticsearch!")
322
+ logger.info("Reconnected to elasticsearch!")
323
323
 
324
324
  if updated:
325
325
  ret_val["updated"] += updated
@@ -331,7 +331,7 @@ class ESCollection(Generic[ModelType]):
331
331
  except elasticsearch.exceptions.NotFoundError as e:
332
332
  if "index_not_found_exception" in str(e):
333
333
  time.sleep(min(retries, self.MAX_RETRY_BACKOFF))
334
- log.debug("The index does not exist. Trying to recreate it...")
334
+ logger.debug("The index does not exist. Trying to recreate it...")
335
335
  self._ensure_collection()
336
336
  self.datastore.connection_reset()
337
337
  retries += 1
@@ -351,7 +351,7 @@ class ESCollection(Generic[ModelType]):
351
351
  retries += 1
352
352
 
353
353
  except elasticsearch.exceptions.ConnectionTimeout:
354
- log.warning(
354
+ logger.warning(
355
355
  f"Elasticsearch connection timeout, server(s): "
356
356
  f"{' | '.join(self.datastore.get_hosts(safe=True))}"
357
357
  f", retrying {func.__name__}..."
@@ -366,7 +366,7 @@ class ESCollection(Generic[ModelType]):
366
366
  elasticsearch.exceptions.AuthenticationException,
367
367
  ) as e:
368
368
  if not isinstance(e, SearchRetryException):
369
- log.warning(
369
+ logger.warning(
370
370
  f"No connection to Elasticsearch server(s): "
371
371
  f"{' | '.join(self.datastore.get_hosts(safe=True))}"
372
372
  f", because [{e}] retrying {func.__name__}..."
@@ -379,19 +379,19 @@ class ESCollection(Generic[ModelType]):
379
379
  except elasticsearch.exceptions.TransportError as e:
380
380
  err_code, msg, cause = e.args
381
381
  if err_code == 503 or err_code == "503":
382
- log.warning(f"Looks like index {self.name} is not ready yet, retrying...")
382
+ logger.warning(f"Looks like index {self.name} is not ready yet, retrying...")
383
383
  time.sleep(min(retries, self.MAX_RETRY_BACKOFF))
384
384
  self.datastore.connection_reset()
385
385
  retries += 1
386
386
  elif err_code == 429 or err_code == "429":
387
- log.warning(
387
+ logger.warning(
388
388
  "Elasticsearch is too busy to perform the requested " f"task on index {self.name}, retrying..."
389
389
  )
390
390
  time.sleep(min(retries, self.MAX_RETRY_BACKOFF))
391
391
  self.datastore.connection_reset()
392
392
  retries += 1
393
393
  elif err_code == 403 or err_code == "403":
394
- log.warning(
394
+ logger.warning(
395
395
  "Elasticsearch cluster is preventing writing operations " f"on index {self.name}, retrying..."
396
396
  )
397
397
  time.sleep(min(retries, self.MAX_RETRY_BACKOFF))
@@ -445,7 +445,7 @@ class ESCollection(Generic[ModelType]):
445
445
  except elasticsearch.exceptions.TransportError as e:
446
446
  err_code, _, _ = e.args
447
447
  if err_code == 408 or err_code == "408":
448
- log.warning(f"Waiting for index {index} to get to status {min_status}...")
448
+ logger.warning(f"Waiting for index {index} to get to status {min_status}...")
449
449
  else:
450
450
  raise
451
451
 
@@ -534,15 +534,12 @@ class ESCollection(Generic[ModelType]):
534
534
  "acknowledged"
535
535
  ]
536
536
 
537
- def fix_shards(self, logger=None):
537
+ def fix_shards(self):
538
538
  """This function should be overloaded to fix the shard configuration of the index of all the different hosts
539
539
  specified in self.datastore.hosts.
540
540
 
541
541
  :return: Should return True of the fix was successful on all hosts
542
542
  """
543
- if logger is None:
544
- logger = log
545
-
546
543
  body = {"settings": self._get_index_settings()}
547
544
  clone_body = {"settings": {"index.number_of_replicas": 0}}
548
545
  clone_finish_settings = None
@@ -844,7 +841,7 @@ class ESCollection(Generic[ModelType]):
844
841
  key_list.remove(row["_id"])
845
842
  add_to_output(row["_source"], row["_id"])
846
843
  except ValueError:
847
- log.exception(f'MGet returned multiple documents for id: {row["_id"]}')
844
+ logger.exception(f'MGet returned multiple documents for id: {row["_id"]}')
848
845
 
849
846
  if key_list and error_on_missing:
850
847
  raise MultiKeyError(key_list, out)
@@ -1221,15 +1218,15 @@ class ESCollection(Generic[ModelType]):
1221
1218
  f"{res['_seq_no']}---{res['_primary_term']}",
1222
1219
  )
1223
1220
  except elasticsearch.NotFoundError as e:
1224
- log.warning("Update - elasticsearch.NotFoundError: %s %s", e.message, e.info)
1221
+ logger.warning("Update - elasticsearch.NotFoundError: %s %s", e.message, e.info)
1225
1222
  except elasticsearch.BadRequestError as e:
1226
- log.warning("Update - elasticsearch.BadRequestError: %s %s", e.message, e.info)
1223
+ logger.warning("Update - elasticsearch.BadRequestError: %s %s", e.message, e.info)
1227
1224
  return False
1228
1225
  except VersionConflictException as e:
1229
- log.warning("Update - elasticsearch.ConflictError: %s", e.message)
1226
+ logger.warning("Update - elasticsearch.ConflictError: %s", e.message)
1230
1227
  raise
1231
1228
  except Exception as e:
1232
- log.warning("Update - Generic Exception: %s", str(e))
1229
+ logger.warning("Update - Generic Exception: %s", str(e))
1233
1230
  return False
1234
1231
 
1235
1232
  return False
@@ -2147,7 +2144,12 @@ class ESCollection(Generic[ModelType]):
2147
2144
  if "total_fields" not in settings["index"]["mapping"]:
2148
2145
  settings["index"]["mapping"]["total_fields"] = {}
2149
2146
 
2150
- settings["index"]["mapping"]["total_fields"]["limit"] = 1500
2147
+ limit = len(self.model_class.flat_fields()) + 500 if self.model_class else 1500
2148
+ if limit < 1500:
2149
+ limit = 1500
2150
+ else:
2151
+ logger.warning("ODM field size is larger than 1500 - set to %s", limit)
2152
+ settings["index"]["mapping"]["total_fields"]["limit"] = limit
2151
2153
 
2152
2154
  return settings
2153
2155
 
@@ -2200,7 +2202,26 @@ class ESCollection(Generic[ModelType]):
2200
2202
 
2201
2203
  missing = set(model.keys()) - set(fields.keys())
2202
2204
  if missing:
2203
- self._add_fields({key: model[key] for key in missing})
2205
+ # TODO: Bump mapping limit
2206
+ try:
2207
+ self._add_fields({key: model[key] for key in missing})
2208
+ except elasticsearch.BadRequestError as err:
2209
+ handled = False
2210
+ if err.body and isinstance(err.body, dict) and "error" in err.body and "reason" in err.body["error"]:
2211
+ reason: str = err.body["error"]["reason"]
2212
+ if reason.startswith("Limit of total fields"):
2213
+ current_count = int(re.sub(r".+\[(\d+)].+", r"\1", reason))
2214
+ logger.warning(
2215
+ "Current field cap %s is too low, increasing to %s", current_count, current_count + 500
2216
+ )
2217
+ self.with_retries(
2218
+ self.datastore.client.indices.put_settings,
2219
+ body={"settings": {"index.mapping.total_fields.limit": current_count + 500}},
2220
+ )
2221
+ self._add_fields({key: model[key] for key in missing})
2222
+ handled = True
2223
+ if not handled:
2224
+ raise
2204
2225
 
2205
2226
  matching = set(fields.keys()) & set(model.keys())
2206
2227
  for field_name in matching:
@@ -2224,7 +2245,7 @@ class ESCollection(Generic[ModelType]):
2224
2245
  """
2225
2246
  # Create HOT index
2226
2247
  if not self.with_retries(self.datastore.client.indices.exists, index=self.name):
2227
- log.debug(f"Index {self.name.upper()} does not exists. Creating it now...")
2248
+ logger.debug(f"Index {self.name.upper()} does not exists. Creating it now...")
2228
2249
  try:
2229
2250
  self.with_retries(
2230
2251
  self.datastore.client.indices.create,
@@ -2235,7 +2256,7 @@ class ESCollection(Generic[ModelType]):
2235
2256
  except elasticsearch.exceptions.RequestError as e:
2236
2257
  if "resource_already_exists_exception" not in str(e):
2237
2258
  raise
2238
- log.warning(f"Tried to create an index template that already exists: {self.name.upper()}")
2259
+ logger.warning(f"Tried to create an index template that already exists: {self.name.upper()}")
2239
2260
 
2240
2261
  self.with_retries(
2241
2262
  self.datastore.client.indices.put_alias,
@@ -2310,7 +2331,7 @@ class ESCollection(Generic[ModelType]):
2310
2331
 
2311
2332
  :return:
2312
2333
  """
2313
- log.debug("Wipe operation started for collection: %s" % self.name.upper())
2334
+ logger.debug("Wipe operation started for collection: %s" % self.name.upper())
2314
2335
 
2315
2336
  for index in self.index_list:
2316
2337
  if self.with_retries(self.datastore.client.indices.exists, index=index):
@@ -5,7 +5,7 @@ import elasticapm
5
5
  import elasticsearch
6
6
 
7
7
  from howler.common.exceptions import HowlerAttributeError
8
- from howler.datastore.collection import ESCollection, log
8
+ from howler.datastore.collection import ESCollection, logger
9
9
  from howler.odm.models.action import Action
10
10
  from howler.odm.models.analytic import Analytic
11
11
  from howler.odm.models.dossier import Dossier
@@ -106,7 +106,7 @@ class HowlerDatastore(object):
106
106
  elasticsearch.exceptions.ConnectionTimeout,
107
107
  elasticsearch.exceptions.AuthenticationException,
108
108
  ):
109
- log.warning(
109
+ logger.warning(
110
110
  f"No connection to Elasticsearch server(s): "
111
111
  f"{' | '.join(self.ds.get_hosts(safe=True))}"
112
112
  f", retrying..."
@@ -118,12 +118,12 @@ class HowlerDatastore(object):
118
118
  except elasticsearch.exceptions.TransportError as e:
119
119
  err_code, msg, cause = e.args
120
120
  if err_code == 503 or err_code == "503":
121
- log.warning("Looks like index is not ready yet, retrying...")
121
+ logger.warning("Looks like index is not ready yet, retrying...")
122
122
  time.sleep(min(retries, max_retry_backoff))
123
123
  self.ds.connection_reset()
124
124
  retries += 1
125
125
  elif err_code == 429 or err_code == "429":
126
- log.warning(
126
+ logger.warning(
127
127
  "Elasticsearch is too busy to perform the requested task, " "we will wait a bit and retry..."
128
128
  )
129
129
  time.sleep(min(retries, max_retry_backoff))
@@ -5,11 +5,7 @@ import chevron
5
5
  import requests
6
6
  from flask import request
7
7
 
8
- from howler.common.exceptions import (
9
- AuthenticationException,
10
- HowlerRuntimeError,
11
- HowlerValueError,
12
- )
8
+ from howler.common.exceptions import AuthenticationException, HowlerRuntimeError, HowlerValueError
13
9
  from howler.common.logging import get_logger
14
10
  from howler.config import cache, config
15
11
  from howler.odm.models.analytic import Analytic
@@ -29,7 +25,7 @@ def get_token(access_token: str) -> str:
29
25
  get_notebook_token = module.get_notebook_token
30
26
  break
31
27
  except ImportError:
32
- logger.exception("Plugin %s does not modify the notebook access token.")
28
+ logger.info("Plugin %s does not modify the notebook access token.")
33
29
 
34
30
  if get_notebook_token:
35
31
  notebook_access_token = get_notebook_token(access_token)
@@ -147,7 +147,7 @@ suppress-none-returning = true
147
147
  [tool.poetry]
148
148
  package-mode = true
149
149
  name = "howler-api"
150
- version = "2.10.0.dev65"
150
+ version = "2.10.0.dev80"
151
151
  description = "Howler - API server"
152
152
  authors = [
153
153
  "Canadian Centre for Cyber Security <howler@cyber.gc.ca>",