howler-api 3.2.0.dev467__tar.gz → 3.2.0.dev470__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 (198) hide show
  1. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/PKG-INFO +15 -17
  2. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/__init__.py +2 -2
  3. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/transition.py +1 -1
  4. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/classification.py +3 -3
  5. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/logging/__init__.py +2 -2
  6. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/collection.py +4 -4
  7. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/store.py +1 -1
  8. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/generate_sigma_rules.py +1 -1
  9. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/oauth.py +3 -3
  10. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/workflow.py +4 -4
  11. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/ws.py +1 -1
  12. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/base.py +7 -9
  13. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/config.py +1 -2
  14. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/client.py +3 -4
  15. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/code_signature.py +1 -3
  16. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/email.py +7 -8
  17. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/file.py +1 -2
  18. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/geo.py +1 -3
  19. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/rule.py +1 -2
  20. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/url.py +2 -4
  21. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/howler_data.py +2 -3
  22. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/randomizer.py +1 -1
  23. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/priority.py +1 -1
  24. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/analytic_service.py +1 -1
  25. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/user_service.py +1 -1
  26. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/pyproject.toml +23 -26
  27. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/README.md +0 -0
  28. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/__init__.py +0 -0
  29. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/add_label.py +0 -0
  30. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/add_to_bundle.py +0 -0
  31. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/change_field.py +0 -0
  32. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/demote.py +0 -0
  33. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/example_plugin.py +0 -0
  34. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/prioritization.py +0 -0
  35. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/promote.py +0 -0
  36. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/remove_from_bundle.py +0 -0
  37. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/actions/remove_label.py +0 -0
  38. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/__init__.py +0 -0
  39. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/base.py +0 -0
  40. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/socket.py +0 -0
  41. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/__init__.py +0 -0
  42. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/action.py +0 -0
  43. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/analytic.py +0 -0
  44. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/auth.py +0 -0
  45. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/clue.py +0 -0
  46. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/configs.py +0 -0
  47. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/dossier.py +0 -0
  48. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/help.py +0 -0
  49. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/hit.py +0 -0
  50. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/notebook.py +0 -0
  51. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/overview.py +0 -0
  52. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/search.py +0 -0
  53. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/template.py +0 -0
  54. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/tool.py +0 -0
  55. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/user.py +0 -0
  56. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/utils/__init__.py +0 -0
  57. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/utils/etag.py +0 -0
  58. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/api/v1/view.py +0 -0
  59. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/app.py +0 -0
  60. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/README.md +0 -0
  61. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/__init__.py +0 -0
  62. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/classification.yml +0 -0
  63. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/exceptions.py +0 -0
  64. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/loader.py +0 -0
  65. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/logging/audit.py +0 -0
  66. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/logging/format.py +0 -0
  67. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/net.py +0 -0
  68. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/net_static.py +0 -0
  69. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/random_user.py +0 -0
  70. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/common/swagger.py +0 -0
  71. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/config.py +0 -0
  72. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/cronjobs/__init__.py +0 -0
  73. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/cronjobs/retention.py +0 -0
  74. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/cronjobs/rules.py +0 -0
  75. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/cronjobs/view_cleanup.py +0 -0
  76. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/README.md +0 -0
  77. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/__init__.py +0 -0
  78. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/bulk.py +0 -0
  79. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/constants.py +0 -0
  80. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/exceptions.py +0 -0
  81. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/howler_store.py +0 -0
  82. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/migrations/fix_process.py +0 -0
  83. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/operations.py +0 -0
  84. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/schemas.py +0 -0
  85. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/support/__init__.py +0 -0
  86. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/support/build.py +0 -0
  87. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/support/schemas.py +0 -0
  88. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/datastore/types.py +0 -0
  89. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/error.py +0 -0
  90. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/__init__.py +0 -0
  91. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/generate_mitre.py +0 -0
  92. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/generate_tlds.py +0 -0
  93. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/reindex_data.py +0 -0
  94. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/external/wipe_databases.py +0 -0
  95. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/gunicorn_config.py +0 -0
  96. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/healthz.py +0 -0
  97. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/__init__.py +0 -0
  98. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/azure.py +0 -0
  99. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/discover.py +0 -0
  100. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/hit.py +0 -0
  101. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/helper/search.py +0 -0
  102. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/README.md +0 -0
  103. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/__init__.py +0 -0
  104. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/charter.txt +0 -0
  105. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/helper.py +0 -0
  106. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/howler_enum.py +0 -0
  107. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/__init__.py +0 -0
  108. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/action.py +0 -0
  109. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/analytic.py +0 -0
  110. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/assemblyline.py +0 -0
  111. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/aws.py +0 -0
  112. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/azure.py +0 -0
  113. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/cbs.py +0 -0
  114. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/clue.py +0 -0
  115. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/dossier.py +0 -0
  116. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/__init__.py +0 -0
  117. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/agent.py +0 -0
  118. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/autonomous_system.py +0 -0
  119. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/cloud.py +0 -0
  120. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/container.py +0 -0
  121. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/dns.py +0 -0
  122. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/egress.py +0 -0
  123. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/elf.py +0 -0
  124. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/error.py +0 -0
  125. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/event.py +0 -0
  126. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/faas.py +0 -0
  127. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/group.py +0 -0
  128. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/hash.py +0 -0
  129. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/host.py +0 -0
  130. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/http.py +0 -0
  131. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/ingress.py +0 -0
  132. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/interface.py +0 -0
  133. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/network.py +0 -0
  134. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/observer.py +0 -0
  135. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/organization.py +0 -0
  136. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/os.py +0 -0
  137. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/pe.py +0 -0
  138. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/process.py +0 -0
  139. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/registry.py +0 -0
  140. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/related.py +0 -0
  141. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/server.py +0 -0
  142. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/threat.py +0 -0
  143. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/tls.py +0 -0
  144. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/user.py +0 -0
  145. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/user_agent.py +0 -0
  146. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/ecs/vulnerability.py +0 -0
  147. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/gcp.py +0 -0
  148. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/hit.py +0 -0
  149. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/lead.py +0 -0
  150. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/localized_label.py +0 -0
  151. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/overview.py +0 -0
  152. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/pivot.py +0 -0
  153. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/template.py +0 -0
  154. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/user.py +0 -0
  155. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/models/view.py +0 -0
  156. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/odm/random_data.py +0 -0
  157. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/patched.py +0 -0
  158. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/plugins/__init__.py +0 -0
  159. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/plugins/config.py +0 -0
  160. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/__init__.py +0 -0
  161. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/README.md +0 -0
  162. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/__init__.py +0 -0
  163. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/counters.py +0 -0
  164. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/events.py +0 -0
  165. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/hash.py +0 -0
  166. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/lock.py +0 -0
  167. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/__init__.py +0 -0
  168. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/comms.py +0 -0
  169. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/multi.py +0 -0
  170. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/queues/named.py +0 -0
  171. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/set.py +0 -0
  172. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/remote/datatypes/user_quota_tracker.py +0 -0
  173. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/security/__init__.py +0 -0
  174. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/security/socket.py +0 -0
  175. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/security/utils.py +0 -0
  176. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/__init__.py +0 -0
  177. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/action_service.py +0 -0
  178. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/auth_service.py +0 -0
  179. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/config_service.py +0 -0
  180. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/dossier_service.py +0 -0
  181. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/event_service.py +0 -0
  182. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/hit_service.py +0 -0
  183. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/jwt_service.py +0 -0
  184. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/lucene_service.py +0 -0
  185. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/notebook_service.py +0 -0
  186. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/overview_service.py +0 -0
  187. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/services/template_service.py +0 -0
  188. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/__init__.py +0 -0
  189. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/annotations.py +0 -0
  190. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/chunk.py +0 -0
  191. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/dict_utils.py +0 -0
  192. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/isotime.py +0 -0
  193. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/list_utils.py +0 -0
  194. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/lucene.py +0 -0
  195. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/path.py +0 -0
  196. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/socket_utils.py +0 -0
  197. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/str_utils.py +0 -0
  198. {howler_api-3.2.0.dev467 → howler_api-3.2.0.dev470}/howler/utils/uid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howler-api
3
- Version: 3.2.0.dev467
3
+ Version: 3.2.0.dev470
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -19,40 +19,38 @@ Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
20
  Classifier: Programming Language :: Python :: 3.14
21
21
  Classifier: Topic :: Software Development :: Libraries
22
- Requires-Dist: apscheduler (==3.10.4)
22
+ Requires-Dist: apscheduler (==3.11.2)
23
23
  Requires-Dist: authlib (>=1.6.0,<2.0.0)
24
- Requires-Dist: azure-identity (==1.16.1)
25
- Requires-Dist: azure-storage-blob (==12.14.1)
26
- Requires-Dist: chardet (==5.1.0)
24
+ Requires-Dist: bcrypt (==4.3.0)
25
+ Requires-Dist: chardet (==5.2.0)
27
26
  Requires-Dist: chevron (==0.14.0)
28
27
  Requires-Dist: elastic-apm[flask] (>=6.22.0,<7.0.0)
29
- Requires-Dist: elasticsearch (==8.6.1)
28
+ Requires-Dist: elasticsearch (==8.19.3)
30
29
  Requires-Dist: flasgger (>=0.9.7.1,<0.10.0.0)
31
- Requires-Dist: flask (==2.2.5)
32
- Requires-Dist: flask-caching (==2.0.2)
30
+ Requires-Dist: flask (==2.3.3)
31
+ Requires-Dist: flask-caching (==2.3.1)
33
32
  Requires-Dist: gevent (==23.9.1)
34
33
  Requires-Dist: gunicorn (==23.0.0)
35
34
  Requires-Dist: luqum (>=1.0.0,<2.0.0)
36
35
  Requires-Dist: mergedeep (>=1.3.4,<2.0.0)
37
36
  Requires-Dist: packaging (<25.0)
38
37
  Requires-Dist: passlib (==1.7.4)
39
- Requires-Dist: prometheus-client (==0.17.1)
38
+ Requires-Dist: prometheus-client (==0.24.1)
40
39
  Requires-Dist: pydantic (>=2.11.4,<3.0.0)
41
40
  Requires-Dist: pydantic-settings[yaml] (>=2.9.1,<3.0.0)
42
41
  Requires-Dist: pydash (>=8.0.5,<9.0.0)
43
- Requires-Dist: pyjwt (==2.6.0)
44
- Requires-Dist: pyroute2-core (==0.6.13)
45
- Requires-Dist: pysftp (==0.2.9)
46
- Requires-Dist: pysigma (==0.11.17)
42
+ Requires-Dist: pyjwt (==2.11.0)
43
+ Requires-Dist: pysigma (==0.11.23)
47
44
  Requires-Dist: pysigma-backend-elasticsearch (>=1.1.2,<2.0.0)
48
45
  Requires-Dist: python-baseconv (==1.2.2)
49
46
  Requires-Dist: python-datemath (==3.0.3)
50
47
  Requires-Dist: python-dotenv (>=1.1.0,<2.0.0)
51
- Requires-Dist: pyyaml (==6.0.2)
52
- Requires-Dist: redis (==4.5.4)
53
- Requires-Dist: requests (==2.32.4)
48
+ Requires-Dist: pytz (>=2025.2,<2026.0)
49
+ Requires-Dist: pyyaml (==6.0.3)
50
+ Requires-Dist: redis (==4.6.0)
51
+ Requires-Dist: requests (==2.32.5)
54
52
  Requires-Dist: typing-extensions (>=4.12.2,<5.0.0)
55
- Requires-Dist: validators (>=0.34.0,<0.35.0)
53
+ Requires-Dist: validators (>=0.34,<0.36)
56
54
  Requires-Dist: wsproto (==1.2.0)
57
55
  Project-URL: Documentation, https://cybercentrecanada.github.io/howler/developer/backend/
58
56
  Project-URL: Homepage, https://cybercentrecanada.github.io/howler/
@@ -51,9 +51,9 @@ def __sanitize_report(report: list[dict[str, Any]]) -> list[dict[str, Any]]:
51
51
  key = f"{entry['title']}==={entry['message']}==={entry['outcome']}"
52
52
 
53
53
  if key in by_message:
54
- by_message[key].append(f'({entry["query"]})')
54
+ by_message[key].append(f"({entry['query']})")
55
55
  else:
56
- by_message[key] = [f'({entry["query"]})']
56
+ by_message[key] = [f"({entry['query']})"]
57
57
 
58
58
  sanitized: list[dict[str, Any]] = []
59
59
  for key, queries in by_message.items():
@@ -41,7 +41,7 @@ def __parse_workflow_actions(workflow: Workflow) -> dict[str, set[str]]:
41
41
  )
42
42
 
43
43
  for key in wf_args:
44
- entry = f'transition:{str(wf["transition"])}'
44
+ entry = f"transition:{str(wf['transition'])}"
45
45
 
46
46
  if key in parsed_args:
47
47
  parsed_args[key].add(entry)
@@ -103,7 +103,7 @@ class Classification(object):
103
103
  self.NULL_CLASSIFICATION,
104
104
  ]:
105
105
  raise InvalidDefinition(
106
- "You cannot use reserved words NULL, INVALID or INV in your " "classification definition."
106
+ "You cannot use reserved words NULL, INVALID or INV in your classification definition."
107
107
  )
108
108
 
109
109
  lvl = int(x["lvl"])
@@ -251,14 +251,14 @@ class Classification(object):
251
251
  return self.levels_map[self.levels_aliases[lvl]]
252
252
  else:
253
253
  raise InvalidClassification(
254
- "Classification level '%s' was not found in " "your classification definition." % lvl
254
+ "Classification level '%s' was not found in your classification definition." % lvl
255
255
  )
256
256
 
257
257
  def _get_c12n_level_text(self, lvl_idx: int, long_format: bool = True) -> str:
258
258
  text = self.levels_map.get(str(lvl_idx), None)
259
259
  if not text:
260
260
  raise InvalidClassification(
261
- "Classification level number '%s' was not " "found in your classification definition." % lvl_idx
261
+ "Classification level number '%s' was not found in your classification definition." % lvl_idx
262
262
  )
263
263
  if long_format:
264
264
  return self.levels_map_stl[text]
@@ -228,8 +228,8 @@ def log_with_traceback(traceback, msg, is_exception=False, audit=False):
228
228
 
229
229
  try:
230
230
  message = (
231
- f'{tb_user["uname"]} [{tb_user["classification"]}] :: {msg} - {tb_file}:{tb_function}:{tb_line_no}'
232
- f'[{os.environ.get("HOWLER_VERSION", "0.0.0.dev0")}] ({request.path}{args})'
231
+ f"{tb_user['uname']} [{tb_user['classification']}] :: {msg} - {tb_file}:{tb_function}:{tb_line_no}"
232
+ f"[{os.environ.get('HOWLER_VERSION', '0.0.0.dev0')}] ({request.path}{args})"
233
233
  )
234
234
  if is_exception:
235
235
  log.exception(message)
@@ -399,14 +399,14 @@ class ESCollection(Generic[ModelType]):
399
399
  retries += 1
400
400
  elif err_code == 429 or err_code == "429":
401
401
  logger.warning(
402
- "Elasticsearch is too busy to perform the requested " f"task on index {self.name}, retrying..."
402
+ f"Elasticsearch is too busy to perform the requested task on index {self.name}, retrying..."
403
403
  )
404
404
  time.sleep(min(retries, self.MAX_RETRY_BACKOFF))
405
405
  self.datastore.connection_reset()
406
406
  retries += 1
407
407
  elif err_code == 403 or err_code == "403":
408
408
  logger.warning(
409
- "Elasticsearch cluster is preventing writing operations " f"on index {self.name}, retrying..."
409
+ f"Elasticsearch cluster is preventing writing operations on index {self.name}, retrying..."
410
410
  )
411
411
  time.sleep(min(retries, self.MAX_RETRY_BACKOFF))
412
412
  self.datastore.connection_reset()
@@ -842,7 +842,7 @@ class ESCollection(Generic[ModelType]):
842
842
  key_list.remove(row["_id"])
843
843
  add_to_output(row["_source"], row["_id"])
844
844
  except ValueError:
845
- logger.exception(f'MGet returned multiple documents for id: {row["_id"]}')
845
+ logger.exception(f"MGet returned multiple documents for id: {row['_id']}")
846
846
 
847
847
  if key_list and error_on_missing:
848
848
  raise MultiKeyError(key_list, out)
@@ -2409,7 +2409,7 @@ class ESCollection(Generic[ModelType]):
2409
2409
  # simply raise an exception
2410
2410
  if no_fix:
2411
2411
  raise HowlerValueError(
2412
- f"Can't update database mapping for {self.name}, " f"couldn't safely amend mapping for {no_fix}"
2412
+ f"Can't update database mapping for {self.name}, couldn't safely amend mapping for {no_fix}"
2413
2413
  )
2414
2414
 
2415
2415
  # If we got this far, the missing fields have been described in properties, upload them to the
@@ -247,7 +247,7 @@ class ESStore(object):
247
247
  name_match = re.match(r"[a-z0-9_]*", name)
248
248
  if not name_match or name_match.string != name:
249
249
  raise DataStoreException(
250
- "Invalid characters in model name. " "You can only use lower case letters, numbers and underscores."
250
+ "Invalid characters in model name. You can only use lower case letters, numbers and underscores."
251
251
  )
252
252
 
253
253
  self._models[name] = model_class
@@ -18,7 +18,7 @@ def main():
18
18
 
19
19
  print("Copying files")
20
20
  for network_yaml in (git_dir / "rules" / "network").glob("**/*.yml"):
21
- print(f" {network_yaml.relative_to(((git_dir / 'rules' / 'network')))}")
21
+ print(f" {network_yaml.relative_to((git_dir / 'rules' / 'network'))}")
22
22
  new_file = output_dir / network_yaml.name
23
23
  shutil.copyfile(network_yaml, new_file)
24
24
 
@@ -183,7 +183,7 @@ def fetch_avatar( # noqa: C901
183
183
 
184
184
  if resp.ok and resp.headers.get("content-type") is not None:
185
185
  b64_img = base64.b64encode(resp.content).decode()
186
- avatar = f'data:{resp.headers.get("content-type")};base64,{b64_img}'
186
+ avatar = f"data:{resp.headers.get('content-type')};base64,{b64_img}"
187
187
  return avatar
188
188
 
189
189
  # Url that is protected through OAuth
@@ -191,7 +191,7 @@ def fetch_avatar( # noqa: C901
191
191
  resp = provider.get(url[len(provider_config.api_base_url) :])
192
192
  if resp.ok and resp.headers.get("content-type") is not None:
193
193
  b64_img = base64.b64encode(resp.content).decode()
194
- avatar = f'data:{resp.headers.get("content-type")};base64,{b64_img}'
194
+ avatar = f"data:{resp.headers.get('content-type')};base64,{b64_img}"
195
195
  return avatar
196
196
 
197
197
  # Unprotected url
@@ -199,7 +199,7 @@ def fetch_avatar( # noqa: C901
199
199
  resp = requests.get(url, timeout=10)
200
200
  if resp.ok and resp.headers.get("content-type") is not None:
201
201
  b64_img = base64.b64encode(resp.content).decode()
202
- avatar = f'data:{resp.headers.get("content-type")};base64,{b64_img}'
202
+ avatar = f"data:{resp.headers.get('content-type')};base64,{b64_img}"
203
203
  return avatar
204
204
 
205
205
  # Quietly fail, it'll use gravatar instead
@@ -51,11 +51,11 @@ class Workflow:
51
51
  for t in transitions:
52
52
  if t.get("source", False) and isinstance(t["source"], list):
53
53
  for s in t["source"]:
54
- self.transitions[f'{s}{t["transition"]}'] = t
55
- identifiers.append(f'{s}{t["transition"]}{t.get("dest", None) or ""}')
54
+ self.transitions[f"{s}{t['transition']}"] = t
55
+ identifiers.append(f"{s}{t['transition']}{t.get('dest', None) or ''}")
56
56
  else:
57
- self.transitions[f'{t.get("source", "") or ""}{t["transition"]}'] = t
58
- identifiers.append(f'{t.get("source", "") or ""}{t["transition"]}{t.get("dest", "") or ""}')
57
+ self.transitions[f"{t.get('source', '') or ''}{t['transition']}"] = t
58
+ identifiers.append(f"{t.get('source', '') or ''}{t['transition']}{t.get('dest', '') or ''}")
59
59
 
60
60
  if len(set(identifiers)) != len(identifiers):
61
61
  raise WorkflowException("There are duplicate transitions (same source, transition and dest values).")
@@ -33,7 +33,7 @@ class ConnectionClosed(RuntimeError):
33
33
  def __init__(self, reason=CloseReason.NO_STATUS_RCVD, message=None):
34
34
  self.reason = reason
35
35
  self.message = message
36
- super().__init__(f'Connection closed: {reason} {message or ""}')
36
+ super().__init__(f"Connection closed: {reason} {message or ''}")
37
37
 
38
38
 
39
39
  class Base:
@@ -202,7 +202,7 @@ class _Field:
202
202
 
203
203
  def check(self, value, **kwargs):
204
204
  raise HowlerNotImplementedError(
205
- "This function is not defined in the default field. " "Each fields has to have their own definition"
205
+ "This function is not defined in the default field. Each fields has to have their own definition"
206
206
  )
207
207
 
208
208
  def __repr__(self) -> str:
@@ -469,7 +469,7 @@ class Email(Keyword):
469
469
  match = self.validation_regex.match(value)
470
470
  if not is_valid_domain(match.group(1)):
471
471
  raise HowlerValueError(
472
- f"[{'.'.join(context) or self.name}] '{match.group(1)}' in email '{value}'" " is not a valid Domain."
472
+ f"[{'.'.join(context) or self.name}] '{match.group(1)}' in email '{value}' is not a valid Domain."
473
473
  )
474
474
 
475
475
  return value.lower()
@@ -487,14 +487,12 @@ class URI(Keyword):
487
487
  match = self.validation_regex.match(value)
488
488
  if not match:
489
489
  raise HowlerValueError(
490
- f"[{'.'.join(context) or self.name}] '{value}' not match the "
491
- f"validator: {self.validation_regex.pattern}"
490
+ f"[{'.'.join(context) or self.name}] '{value}' not match the validator: {self.validation_regex.pattern}"
492
491
  )
493
492
 
494
493
  if not is_valid_domain(match.group(2)) and not is_valid_ip(match.group(2)):
495
494
  raise HowlerValueError(
496
- f"[{'.'.join(context) or self.name}] '{match.group(2)}' in URI '{value}'"
497
- " is not a valid Domain or IP."
495
+ f"[{'.'.join(context) or self.name}] '{match.group(2)}' in URI '{value}' is not a valid Domain or IP."
498
496
  )
499
497
 
500
498
  return match.group(0).replace(match.group(1), match.group(1).lower())
@@ -1199,7 +1197,7 @@ class Model:
1199
1197
  )
1200
1198
 
1201
1199
  # Header
1202
- markdown_content += f"{'#'*toc_depth} {cls.__name__}\n\n> {cls.__description}\n\n"
1200
+ markdown_content += f"{'#' * toc_depth} {cls.__name__}\n\n> {cls.__description}\n\n"
1203
1201
 
1204
1202
  # Table
1205
1203
  table = "| Field | Type | Description | Required | Default |\n| :--- | :--- | :--- | :--- | :--- |\n"
@@ -1251,7 +1249,7 @@ class Model:
1251
1249
 
1252
1250
  values = [f'"{v}"' if v else str(v) for v in sorted(values)]
1253
1251
  values.append("None") if none_value else None
1254
- description = f'{description}<br>Values:<br>`{", ".join(values)}`'
1252
+ description = f"{description}<br>Values:<br>`{', '.join(values)}`"
1255
1253
 
1256
1254
  # Is this a required field?
1257
1255
  if info.__class__ != Optional and not info.optional:
@@ -1340,7 +1338,7 @@ class Model:
1340
1338
  extra_keys = set(extra_fields.keys()) - set(data.keys())
1341
1339
  if self.unused_keys and not ignore_extra_values:
1342
1340
  raise HowlerValueError(
1343
- f"[{'.'.join(context)}]: object was created with invalid parameters: " f"{', '.join(self.unused_keys)}"
1341
+ f"[{'.'.join(context)}]: object was created with invalid parameters: {', '.join(self.unused_keys)}"
1344
1342
  )
1345
1343
 
1346
1344
  # Pass each value through it's respective validator, and store it
@@ -327,8 +327,7 @@ class Retention(BaseModel):
327
327
  enabled: bool = Field(
328
328
  default=True,
329
329
  description=(
330
- "Whether to enable the hit retention limit. If enabled, hits will "
331
- "be purged after the specified duration."
330
+ "Whether to enable the hit retention limit. If enabled, hits will be purged after the specified duration."
332
331
  ),
333
332
  )
334
333
  limit_unit: Literal["days", "seconds", "microseconds", "milliseconds", "minutes", "hours", "weeks"] = Field(
@@ -45,15 +45,14 @@ class OriginalClient(odm.Model):
45
45
  bytes: Optional[int] = odm.Optional(
46
46
  odm.Integer(
47
47
  description=(
48
- "The original client in a session that has changed clients. "
49
- "Bytes sent from the client to the server."
48
+ "The original client in a session that has changed clients. Bytes sent from the client to the server."
50
49
  )
51
50
  )
52
51
  )
53
52
  domain: Optional[str] = odm.Optional(
54
53
  odm.Domain(
55
54
  description=(
56
- "The original client in a session that has changed clients. " "The domain name of the client system."
55
+ "The original client in a session that has changed clients. The domain name of the client system."
57
56
  )
58
57
  )
59
58
  )
@@ -69,7 +68,7 @@ class OriginalClient(odm.Model):
69
68
  ip: Optional[str] = odm.Optional(
70
69
  odm.IP(
71
70
  description=(
72
- "The original client in a session that has changed clients. IP address of the " "client (IPv4 or IPv6)."
71
+ "The original client in a session that has changed clients. IP address of the client (IPv4 or IPv6)."
73
72
  )
74
73
  )
75
74
  )
@@ -21,7 +21,5 @@ class CodeSignature(odm.Model):
21
21
  timestamp = odm.Optional(odm.Date(description="Date and time when the code signature was generated and signed."))
22
22
  trusted = odm.Optional(odm.Boolean(description="Stores the trust status of the certificate chain."))
23
23
  valid = odm.Optional(
24
- odm.Boolean(
25
- description="Boolean to capture if the digital signature" " is verified against the binary content."
26
- )
24
+ odm.Boolean(description="Boolean to capture if the digital signature is verified against the binary content.")
27
25
  )
@@ -36,7 +36,7 @@ class ParentEmail(odm.Model):
36
36
  from_ = odm.Optional(
37
37
  odm.Compound(
38
38
  Address,
39
- description="The email address of the sender, typically " "from the RFC 5322 From: header field.",
39
+ description="The email address of the sender, typically from the RFC 5322 From: header field.",
40
40
  )
41
41
  )
42
42
  message_id = odm.Optional(
@@ -70,19 +70,19 @@ class Email(odm.Model):
70
70
  cc = odm.Optional(odm.Compound(Address, description="The email address of CC recipient."))
71
71
  content_type = odm.Optional(odm.Keyword(description="Information about how the message is to be displayed."))
72
72
  delivery_timestamp = odm.Optional(
73
- odm.Date(description="The date and time when the email message " "was received by the service or client.")
73
+ odm.Date(description="The date and time when the email message was received by the service or client.")
74
74
  )
75
75
  direction = odm.Optional(
76
- odm.Keyword(description="The direction of the message based on the " "sending and receiving domains.")
76
+ odm.Keyword(description="The direction of the message based on the sending and receiving domains.")
77
77
  )
78
78
  from_ = odm.Optional(
79
79
  odm.Compound(
80
80
  Address,
81
- description="The email address of the sender, typically " "from the RFC 5322 From: header field.",
81
+ description="The email address of the sender, typically from the RFC 5322 From: header field.",
82
82
  )
83
83
  )
84
84
  local_id = odm.Optional(
85
- odm.Keyword(description="Unique identifier given to the email by the source " "that created the event.")
85
+ odm.Keyword(description="Unique identifier given to the email by the source that created the event.")
86
86
  )
87
87
  message_id = odm.Optional(
88
88
  odm.Keyword(
@@ -101,15 +101,14 @@ class Email(odm.Model):
101
101
  sender = odm.Optional(
102
102
  odm.Compound(
103
103
  Address,
104
- description="Per RFC 5322, specifies the address responsible for "
105
- "the actual transmission of the message.",
104
+ description="Per RFC 5322, specifies the address responsible for the actual transmission of the message.",
106
105
  )
107
106
  )
108
107
  subject = odm.Optional(odm.Keyword(description="A brief summary of the topic of the message."))
109
108
  to = odm.Optional(odm.Compound(Address, description="The email address of recipient."))
110
109
  x_mailer = odm.Optional(
111
110
  odm.Keyword(
112
- description="The name of the application that was used to draft " "and send the original email message."
111
+ description="The name of the application that was used to draft and send the original email message."
113
112
  )
114
113
  )
115
114
 
@@ -14,8 +14,7 @@ FILE_TYPE = ["file", "dir", "symlink"]
14
14
  @odm.model(
15
15
  index=True,
16
16
  store=True,
17
- description="A file is defined as a set of information that has "
18
- "been created on, or has existed on a filesystem.",
17
+ description="A file is defined as a set of information that has been created on, or has existed on a filesystem.",
19
18
  )
20
19
  class File(odm.Model):
21
20
  accessed: Optional[str] = odm.Optional(odm.Date(description="Last time the file was accessed."))
@@ -20,9 +20,7 @@ class Geo(odm.Model):
20
20
  country_name = odm.Optional(odm.Keyword(description="Country name."))
21
21
  location = odm.Optional(odm.Compound(GeoPoint, description="Longitude and latitude."))
22
22
  name = odm.Optional(
23
- odm.Keyword(
24
- description="User-defined description of a location, at the level " "of granularity they care about."
25
- )
23
+ odm.Keyword(description="User-defined description of a location, at the level of granularity they care about.")
26
24
  )
27
25
  postal_code = odm.Optional(odm.Keyword(description="Postal code associated with the location."))
28
26
  region_iso_code = odm.Optional(odm.Keyword(description="Region ISO code."))
@@ -16,8 +16,7 @@ class Rule(odm.Model):
16
16
  )
17
17
  category = odm.Optional(
18
18
  odm.Keyword(
19
- description="A categorization value keyword used by the entity using the "
20
- "rule for detection of this event."
19
+ description="A categorization value keyword used by the entity using the rule for detection of this event."
21
20
  )
22
21
  )
23
22
  description = odm.Optional(odm.Keyword(description="The description of the rule generating the event."))
@@ -27,12 +27,10 @@ class URL(odm.Model):
27
27
  path = odm.Optional(odm.Keyword(description='Path of the request, such as "/search".'))
28
28
  port = odm.Optional(odm.Integer(description="Port of the request, such as 443."))
29
29
  query = odm.Optional(
30
- odm.Keyword(
31
- description="The query field describes the query string of the " 'request, such as "q=elasticsearch".'
32
- )
30
+ odm.Keyword(description='The query field describes the query string of the request, such as "q=elasticsearch".')
33
31
  )
34
32
  registered_domain = odm.Optional(
35
- odm.Keyword(description="The highest registered url domain, " "stripped of the subdomain.")
33
+ odm.Keyword(description="The highest registered url domain, stripped of the subdomain.")
36
34
  )
37
35
  scheme = odm.Optional(odm.Keyword(description='Scheme of the request, such as "https".'))
38
36
  subdomain = odm.Optional(
@@ -116,8 +116,7 @@ class Link(odm.Model):
116
116
  title = odm.Keyword(description="The title to use for the link.", optional=True)
117
117
  icon = odm.Keyword(
118
118
  description=(
119
- "The icon to show. Either an ID corresponding to an "
120
- "analytical platform application, or an external link."
119
+ "The icon to show. Either an ID corresponding to an analytical platform application, or an external link."
121
120
  ),
122
121
  optional=True,
123
122
  )
@@ -288,7 +287,7 @@ class HowlerData(odm.Model):
288
287
  rationale: Optional[str] = odm.Optional(
289
288
  odm.Keyword(
290
289
  description=(
291
- "The rationale behind the hit assessment. Allows it to be understood and" " verified by other analysts."
290
+ "The rationale behind the hit assessment. Allows it to be understood and verified by other analysts."
292
291
  )
293
292
  )
294
293
  )
@@ -379,7 +379,7 @@ def get_random_mapping(field: _Field) -> dict[str, _Any]:
379
379
  def get_random_phone() -> str:
380
380
  """Get a random phone"""
381
381
  return (
382
- f'{random.choice(["", "+1 "])}{"-".join([str(random.randint(100, 999)) for _ in range(3)])}'
382
+ f"{random.choice(['', '+1 '])}{'-'.join([str(random.randint(100, 999)) for _ in range(3)])}"
383
383
  f"{str(random.randint(0, 9))}"
384
384
  )
385
385
 
@@ -99,7 +99,7 @@ class PriorityQueue(Generic[T]):
99
99
 
100
100
  def push(self, priority: int, data: T, vip=None):
101
101
  vip = 0 if vip else 9
102
- value = f"{vip}{f'{int(time.time()*1000000):020}'}{json.dumps(data)}"
102
+ value = f"{vip}{f'{int(time.time() * 1000000):020}'}{json.dumps(data)}"
103
103
  retry_call(self.c.zadd, self.name, {value: -priority})
104
104
  return value
105
105
 
@@ -86,7 +86,7 @@ def get_matching_analytics(hits: Union[list[Hit], list[dict[str, Any]]]) -> list
86
86
 
87
87
  try:
88
88
  existing_analytics: list[Analytic] = storage.analytic.search(
89
- f'name:({" OR ".join(analytic_names)})', as_obj=True
89
+ f"name:({' OR '.join(analytic_names)})", as_obj=True
90
90
  )["items"]
91
91
 
92
92
  return existing_analytics
@@ -282,7 +282,7 @@ def add_access_control(user: dict[str, Any]):
282
282
  if req_query:
283
283
  req_query = f"-({req_query}) AND "
284
284
 
285
- lvl_query = f'__access_lvl__:[0 TO {user["__access_lvl__"]}]'
285
+ lvl_query = f"__access_lvl__:[0 TO {user['__access_lvl__']}]"
286
286
 
287
287
  query = f"{gl2_query}{gl1_query}{req_query}{lvl_query}"
288
288
  user["access_control"] = safe_str(query)
@@ -152,7 +152,7 @@ suppress-none-returning = true
152
152
  [tool.poetry]
153
153
  package-mode = true
154
154
  name = "howler-api"
155
- version = "3.2.0.dev467"
155
+ version = "3.2.0.dev470"
156
156
  description = "Howler - API server"
157
157
  authors = [
158
158
  "Canadian Centre for Cyber Security <howler@cyber.gc.ca>",
@@ -196,45 +196,43 @@ repository = "https://github.com/CybercentreCanada/howler-api"
196
196
 
197
197
  [tool.poetry.dependencies]
198
198
  python = "^3.9.17"
199
- apscheduler = "3.10.4"
199
+ apscheduler = "3.11.2"
200
200
  authlib = "^1.6.0"
201
- azure-identity = "1.16.1"
202
- azure-storage-blob = "12.14.1"
203
- chardet = "5.1.0"
201
+ chardet = "5.2.0"
204
202
  elastic-apm = { extras = ["flask"], version = "^6.22.0" }
205
- elasticsearch = "8.6.1"
206
- flask = "2.2.5"
207
- flask-caching = "2.0.2"
203
+ elasticsearch = "8.19.3"
204
+ flask = "2.3.3"
205
+ flask-caching = "2.3.1"
208
206
  gevent = "23.9.1"
209
207
  gunicorn = "23.0.0"
210
208
  packaging = "<25.0"
211
209
  passlib = "1.7.4"
212
- prometheus-client = "0.17.1"
213
- pyjwt = "2.6.0"
214
- pyroute2-core = "0.6.13"
215
- pysftp = "0.2.9"
210
+ prometheus-client = "0.24.1"
211
+ pyjwt = "2.11.0"
216
212
  python-baseconv = "1.2.2"
217
213
  python-datemath = "3.0.3"
218
- pyyaml = "6.0.2"
219
- redis = "4.5.4"
220
- requests = "2.32.4"
214
+ pyyaml = "6.0.3"
215
+ redis = "4.6.0"
216
+ requests = "2.32.5"
221
217
  wsproto = "1.2.0"
222
218
  chevron = "0.14.0"
223
219
  typing-extensions = "^4.12.2"
224
220
  flasgger = "^0.9.7.1"
225
- pysigma = "0.11.17"
221
+ pysigma = "0.11.23"
226
222
  pysigma-backend-elasticsearch = "^1.1.2"
227
223
  mergedeep = "^1.3.4"
228
- validators = "^0.34.0"
224
+ validators = ">=0.34,<0.36"
229
225
  python-dotenv = "^1.1.0"
230
226
  pydantic = "^2.11.4"
231
227
  pydantic-settings = { extras = ["yaml"], version = "^2.9.1" }
232
228
  luqum = "^1.0.0"
233
229
  pydash = "^8.0.5"
230
+ pytz = "^2025.2"
231
+ bcrypt = "4.3.0"
234
232
 
235
233
  [tool.poetry.group.dev.dependencies]
236
234
  pre-commit = "^3.7.0"
237
- ruff = "^0.8.0"
235
+ ruff = ">=0.8,<0.16"
238
236
  pyright = {extras = ["nodejs"], version = "^1.1.408"}
239
237
  mypy = "^1.6.1"
240
238
 
@@ -250,17 +248,16 @@ mypy-extensions = "^1.0.0"
250
248
  coverage = { extras = ["toml"], version = "^7.4.4" }
251
249
 
252
250
  [tool.poetry.group.types.dependencies]
253
- types-PyYAML = "6.0.12.12"
254
- types-paramiko = "3.3.0.0"
255
- types-pyOpenSSL = "23.3.0.0"
256
- types-redis = "4.6.0.9"
257
- types-six = "1.16.21.9"
258
- types-requests = "<2.31.0.7"
259
- websocket-client = "1.5.1"
251
+ types-PyYAML = "6.0.12.20250915"
252
+ types-paramiko = "3.5.0.20250801"
253
+ types-pyOpenSSL = "23.3.0.20240106"
254
+ types-redis = "4.6.0.20241004"
255
+ types-six = "1.17.0.20251009"
256
+ types-requests = "<2.32.4.20260108"
257
+ websocket-client = "1.9.0"
260
258
  types-pytz = "^2024.1.0.20240417"
261
259
  types-mock = "^5.2.0.20250306"
262
260
 
263
-
264
261
  [tool.poetry.group.pre-commit.dependencies]
265
262
  rich = "^14.2.0"
266
263
  conventional-pre-commit = "^4.3.0"