howler-api 4.0.0.dev982__tar.gz → 4.0.0.dev1003__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 (204) hide show
  1. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/PKG-INFO +1 -1
  2. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/search.py +0 -29
  3. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/tool.py +42 -15
  4. howler_api-4.0.0.dev1003/howler/common/net.py +128 -0
  5. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/collection.py +250 -19
  6. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/howler_store.py +2 -1
  7. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/store.py +12 -3
  8. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/config.py +50 -0
  9. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/pyproject.toml +1 -1
  10. howler_api-4.0.0.dev982/howler/common/net.py +0 -79
  11. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/README.md +0 -0
  12. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/__init__.py +0 -0
  13. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/__init__.py +0 -0
  14. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/add_label.py +0 -0
  15. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/add_to_bundle.py +0 -0
  16. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/change_field.py +0 -0
  17. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/demote.py +0 -0
  18. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/example_plugin.py +0 -0
  19. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/prioritization.py +0 -0
  20. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/promote.py +0 -0
  21. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/remove_from_bundle.py +0 -0
  22. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/remove_label.py +0 -0
  23. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/actions/transition.py +0 -0
  24. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/__init__.py +0 -0
  25. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/base.py +0 -0
  26. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/socket.py +0 -0
  27. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/__init__.py +0 -0
  28. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/action.py +0 -0
  29. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/analytic.py +0 -0
  30. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/auth.py +0 -0
  31. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/clue.py +0 -0
  32. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/configs.py +0 -0
  33. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/dossier.py +0 -0
  34. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/help.py +0 -0
  35. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/hit.py +0 -0
  36. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/notebook.py +0 -0
  37. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/overview.py +0 -0
  38. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/template.py +0 -0
  39. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/user.py +0 -0
  40. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/utils/__init__.py +0 -0
  41. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/utils/etag.py +0 -0
  42. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/api/v1/view.py +0 -0
  43. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/app.py +0 -0
  44. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/README.md +0 -0
  45. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/__init__.py +0 -0
  46. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/classification.py +0 -0
  47. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/classification.yml +0 -0
  48. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/exceptions.py +0 -0
  49. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/loader.py +0 -0
  50. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/logging/__init__.py +0 -0
  51. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/logging/audit.py +0 -0
  52. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/logging/format.py +0 -0
  53. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/net_static.py +0 -0
  54. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/random_user.py +0 -0
  55. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/common/swagger.py +0 -0
  56. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/config.py +0 -0
  57. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/cronjobs/__init__.py +0 -0
  58. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/cronjobs/action_queue_worker.py +0 -0
  59. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/cronjobs/retention.py +0 -0
  60. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/cronjobs/rules.py +0 -0
  61. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/cronjobs/view_cleanup.py +0 -0
  62. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/README.md +0 -0
  63. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/__init__.py +0 -0
  64. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/bulk.py +0 -0
  65. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/constants.py +0 -0
  66. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/exceptions.py +0 -0
  67. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/migrations/fix_process.py +0 -0
  68. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/operations.py +0 -0
  69. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/schemas.py +0 -0
  70. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/support/__init__.py +0 -0
  71. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/support/build.py +0 -0
  72. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/support/schemas.py +0 -0
  73. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/datastore/types.py +0 -0
  74. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/error.py +0 -0
  75. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/external/README.md +0 -0
  76. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/external/__init__.py +0 -0
  77. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/external/generate_mitre.py +0 -0
  78. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/external/generate_sigma_rules.py +0 -0
  79. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/external/generate_tlds.py +0 -0
  80. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/external/reindex_data.py +0 -0
  81. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/external/wipe_databases.py +0 -0
  82. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/gunicorn_config.py +0 -0
  83. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/healthz.py +0 -0
  84. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/helper/__init__.py +0 -0
  85. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/helper/azure.py +0 -0
  86. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/helper/discover.py +0 -0
  87. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/helper/hit.py +0 -0
  88. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/helper/oauth.py +0 -0
  89. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/helper/search.py +0 -0
  90. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/helper/workflow.py +0 -0
  91. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/helper/ws.py +0 -0
  92. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/README.md +0 -0
  93. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/__init__.py +0 -0
  94. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/base.py +0 -0
  95. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/charter.txt +0 -0
  96. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/helper.py +0 -0
  97. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/howler_enum.py +0 -0
  98. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/__init__.py +0 -0
  99. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/action.py +0 -0
  100. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/analytic.py +0 -0
  101. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/assemblyline.py +0 -0
  102. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/aws.py +0 -0
  103. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/azure.py +0 -0
  104. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/cbs.py +0 -0
  105. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/clue.py +0 -0
  106. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/dossier.py +0 -0
  107. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/__init__.py +0 -0
  108. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/agent.py +0 -0
  109. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/autonomous_system.py +0 -0
  110. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/client.py +0 -0
  111. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/cloud.py +0 -0
  112. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/code_signature.py +0 -0
  113. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/container.py +0 -0
  114. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/dns.py +0 -0
  115. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/egress.py +0 -0
  116. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/elf.py +0 -0
  117. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/email.py +0 -0
  118. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/error.py +0 -0
  119. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/event.py +0 -0
  120. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/faas.py +0 -0
  121. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/file.py +0 -0
  122. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/geo.py +0 -0
  123. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/group.py +0 -0
  124. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/hash.py +0 -0
  125. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/host.py +0 -0
  126. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/http.py +0 -0
  127. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/ingress.py +0 -0
  128. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/interface.py +0 -0
  129. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/network.py +0 -0
  130. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/observer.py +0 -0
  131. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/organization.py +0 -0
  132. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/os.py +0 -0
  133. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/pe.py +0 -0
  134. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/process.py +0 -0
  135. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/registry.py +0 -0
  136. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/related.py +0 -0
  137. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/rule.py +0 -0
  138. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/server.py +0 -0
  139. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/threat.py +0 -0
  140. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/tls.py +0 -0
  141. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/url.py +0 -0
  142. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/user.py +0 -0
  143. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/user_agent.py +0 -0
  144. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/ecs/vulnerability.py +0 -0
  145. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/gcp.py +0 -0
  146. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/hit.py +0 -0
  147. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/howler_data.py +0 -0
  148. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/lead.py +0 -0
  149. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/localized_label.py +0 -0
  150. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/overview.py +0 -0
  151. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/pivot.py +0 -0
  152. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/template.py +0 -0
  153. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/user.py +0 -0
  154. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/models/view.py +0 -0
  155. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/random_data.py +0 -0
  156. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/odm/randomizer.py +0 -0
  157. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/patched.py +0 -0
  158. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/plugins/__init__.py +0 -0
  159. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/plugins/config.py +0 -0
  160. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/__init__.py +0 -0
  161. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/README.md +0 -0
  162. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/__init__.py +0 -0
  163. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/counters.py +0 -0
  164. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/events.py +0 -0
  165. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/hash.py +0 -0
  166. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/lock.py +0 -0
  167. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/queues/__init__.py +0 -0
  168. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/queues/comms.py +0 -0
  169. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/queues/multi.py +0 -0
  170. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/queues/named.py +0 -0
  171. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/queues/priority.py +0 -0
  172. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/set.py +0 -0
  173. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/remote/datatypes/user_quota_tracker.py +0 -0
  174. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/security/__init__.py +0 -0
  175. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/security/socket.py +0 -0
  176. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/security/utils.py +0 -0
  177. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/__init__.py +0 -0
  178. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/action_service.py +0 -0
  179. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/analytic_service.py +0 -0
  180. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/auth_service.py +0 -0
  181. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/config_service.py +0 -0
  182. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/dossier_service.py +0 -0
  183. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/event_service.py +0 -0
  184. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/hit_service.py +0 -0
  185. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/jwt_service.py +0 -0
  186. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/lucene_service.py +0 -0
  187. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/notebook_service.py +0 -0
  188. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/overview_service.py +0 -0
  189. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/template_service.py +0 -0
  190. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/services/user_service.py +0 -0
  191. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/telemetry.py +0 -0
  192. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/__init__.py +0 -0
  193. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/annotations.py +0 -0
  194. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/chunk.py +0 -0
  195. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/compat.py +0 -0
  196. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/constants.py +0 -0
  197. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/dict_utils.py +0 -0
  198. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/isotime.py +0 -0
  199. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/list_utils.py +0 -0
  200. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/lucene.py +0 -0
  201. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/path.py +0 -0
  202. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/socket_utils.py +0 -0
  203. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/howler/utils/str_utils.py +0 -0
  204. {howler_api-4.0.0.dev982 → howler_api-4.0.0.dev1003}/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.dev982
3
+ Version: 4.0.0.dev1003
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -78,7 +78,6 @@ def search(index, **kwargs):
78
78
  sort => How to sort the results (not available in deep paging)
79
79
  fl => List of fields to return
80
80
  timeout => Maximum execution time (ms)
81
- use_archive => Allow access to the datastore achive (Default: False)
82
81
  track_total_hits => Track the total number of query matches, instead of stopping at 10000 (Default: False)
83
82
  metadata => A list of additional features to be added to the result alongside the raw results
84
83
 
@@ -118,18 +117,9 @@ def search(index, **kwargs):
118
117
  "track_total_hits",
119
118
  ]
120
119
  multi_fields = ["filters", "metadata"]
121
- boolean_fields = ["use_archive"]
122
120
 
123
121
  params, req_data = generate_params(request, fields, multi_fields)
124
122
 
125
- params.update(
126
- {
127
- k: str(req_data.get(k, "false")).lower() in ["true", ""]
128
- for k in boolean_fields
129
- if req_data.get(k, None) is not None
130
- }
131
- )
132
-
133
123
  if has_access_control(index):
134
124
  params.update({"access_control": user["access_control"]})
135
125
 
@@ -350,18 +340,9 @@ def sigma_search(index, **kwargs):
350
340
  "track_total_hits",
351
341
  ]
352
342
  multi_fields = ["filters"]
353
- boolean_fields = ["use_archive"]
354
343
 
355
344
  params, req_data = generate_params(request, fields, multi_fields)
356
345
 
357
- params.update(
358
- {
359
- k: str(req_data.get(k, "false")).lower() in ["true", ""]
360
- for k in boolean_fields
361
- if req_data.get(k, None) is not None
362
- }
363
- )
364
-
365
346
  if has_access_control(index):
366
347
  params.update({"access_control": user["access_control"]})
367
348
 
@@ -520,7 +501,6 @@ def count(index, **kwargs):
520
501
  Optional Arguments:
521
502
  filters => List of additional filter queries limit the data
522
503
  timeout => Maximum execution time (ms)
523
- use_archive => Allow access to the datastore achive (Default: False)
524
504
 
525
505
  Data Block:
526
506
  # Note that the data block is for POST requests only!
@@ -543,15 +523,6 @@ def count(index, **kwargs):
543
523
 
544
524
  params, req_data = generate_params(request, [], [])
545
525
 
546
- boolean_fields = ["use_archive"]
547
- params.update(
548
- {
549
- k: str(req_data.get(k, "false")).lower() in ["true", ""]
550
- for k in boolean_fields
551
- if req_data.get(k, None) is not None
552
- }
553
- )
554
-
555
526
  if has_access_control(index):
556
527
  params.update({"access_control": user["access_control"]})
557
528
 
@@ -59,6 +59,9 @@ def create_one_or_many_hits(tool_name: str, user: User, **kwargs): # noqa: C901
59
59
  {'id': None, 'error': "Error message"},
60
60
  ]
61
61
  }
62
+
63
+ .. deprecated::
64
+ Use POST /api/v1/hit/ directly with pre-mapped hit data instead.
62
65
  """
63
66
  data = request.json
64
67
  if not isinstance(data, dict):
@@ -74,7 +77,10 @@ def create_one_or_many_hits(tool_name: str, user: User, **kwargs): # noqa: C901
74
77
 
75
78
  if not isinstance(hits, list):
76
79
  return bad_request(err="Invalid: 'hits' field is missing or invalid.")
77
- warnings = []
80
+ warnings = [
81
+ "This endpoint is deprecated and will be removed in a future version. "
82
+ "Use POST /api/v1/hit/ directly with pre-mapped hit data instead."
83
+ ]
78
84
  # Validate field_map targets
79
85
  hit_fields = Hit.flat_fields()
80
86
  for targets in field_map.values():
@@ -157,27 +163,48 @@ def create_one_or_many_hits(tool_name: str, user: User, **kwargs): # noqa: C901
157
163
  logger.warning(e)
158
164
 
159
165
  out.append({"id": None, "error": str(e)})
166
+
167
+ # Deduplicate by hash: skip hits whose hash already exists in the datastore
168
+ if odms:
169
+ hashes = [odm.howler.hash for odm in odms]
170
+ existing_hashes: dict[str, int] = datastore().hit.facet(
171
+ "howler.hash",
172
+ query=f"howler.hash:({' OR '.join(hashes)})",
173
+ rows=len(hashes),
174
+ )
175
+
176
+ deduplicated_odms = []
177
+ for odm in odms:
178
+ if odm.howler.hash in existing_hashes:
179
+ logger.warning("Hit with hash %s already exists in the DB, skipping", odm.howler.hash)
180
+ warnings.append(f"Hit with hash {odm.howler.hash} already exists in the DB and was skipped.")
181
+ out[:] = [entry for entry in out if entry["id"] != odm.howler.id]
182
+ else:
183
+ deduplicated_odms.append(odm)
184
+
185
+ odms = deduplicated_odms
186
+
160
187
  # If there are any errors...
161
188
  if any([obj["error"] for obj in out]):
162
189
  return bad_request(out, warnings=warnings, err="No valid hits were provided")
163
- else:
164
- for odm in odms:
165
- if bundle_hit is not None:
166
- bundle_hit.howler.hits.append(odm.howler.id)
167
- bundle_hit.howler.bundle_size += 1
168
- odm.howler.bundles.append(bundle_hit.howler.id)
169
190
 
170
- hit_service.create_hit(odm.howler.id, odm, user=user.uname)
191
+ for odm in odms:
192
+ if bundle_hit is not None:
193
+ bundle_hit.howler.hits.append(odm.howler.id)
194
+ bundle_hit.howler.bundle_size += 1
195
+ odm.howler.bundles.append(bundle_hit.howler.id)
196
+
197
+ hit_service.create_hit(odm.howler.id, odm, user=user.uname)
171
198
 
172
- analytic_service.save_from_hit(odm, user)
199
+ analytic_service.save_from_hit(odm, user)
173
200
 
174
- if bundle_hit:
175
- hit_service.create_hit(bundle_hit.howler.id, bundle_hit, user=user.uname)
201
+ if bundle_hit:
202
+ hit_service.create_hit(bundle_hit.howler.id, bundle_hit, user=user.uname)
176
203
 
177
- analytic_service.save_from_hit(bundle_hit, user)
204
+ analytic_service.save_from_hit(bundle_hit, user)
178
205
 
179
- datastore().hit.commit()
206
+ datastore().hit.commit()
180
207
 
181
- action_service.enqueue_action_execution([entry["id"] for entry in out], trigger="create", user=user)
208
+ action_service.enqueue_action_execution([entry["id"] for entry in out], trigger="create", user=user)
182
209
 
183
- return created(out, warnings=warnings)
210
+ return created(out, warnings=warnings)
@@ -0,0 +1,128 @@
1
+ import socket
2
+ import uuid
3
+ from ipaddress import IPv4Network, ip_address
4
+ from typing import Union
5
+
6
+ from howler.common.net_static import TLDS_ALPHA_BY_DOMAIN
7
+
8
+ # IANA Special-Use Domain Names Registry
9
+ # https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
10
+ IANA_SPECIAL_USE_TLDS = {
11
+ "LOCALHOST", # RFC 6761 - loopback address
12
+ "TEST", # RFC 6761 - testing
13
+ "EXAMPLE", # RFC 6761 - documentation examples
14
+ "INVALID", # RFC 6761 - invalid domain names
15
+ "LOCAL", # RFC 6762 - mDNS/Bonjour local network names
16
+ "ONION", # RFC 7686 - Tor hidden services
17
+ }
18
+
19
+ # Common private network suffixes used in enterprise environments
20
+ # These are NOT IANA-registered but are widely used organizational conventions
21
+ PRIVATE_NETWORK_SUFFIXES = {
22
+ "INTERNAL",
23
+ "LAN",
24
+ "HOME",
25
+ "CORP",
26
+ "LOCALDOMAIN",
27
+ }
28
+
29
+
30
+ def is_valid_port(value: Union[int, str, float]) -> bool:
31
+ "Check if a port is valid"
32
+ try:
33
+ if 1 <= int(value) <= 65535:
34
+ return True
35
+ except ValueError:
36
+ pass
37
+
38
+ return False
39
+
40
+
41
+ def is_valid_domain(
42
+ domain: str,
43
+ allow_special_use_tlds: bool = True,
44
+ allow_private_suffixes: bool = True,
45
+ ) -> bool:
46
+ """Check if a domain is valid.
47
+
48
+ Args:
49
+ domain: The domain to validate.
50
+ allow_special_use_tlds: If True, accepts IANA special-use TLDs
51
+ (.localhost, .local, .onion, .test, .example, .invalid).
52
+ Set to False to reject these domains.
53
+ Defaults to True for backward compatibility.
54
+ allow_private_suffixes: If True, accepts common private network suffixes
55
+ (.internal, .lan, .home, .corp, .localdomain).
56
+ Set to False to reject these domains.
57
+ Defaults to True for backward compatibility.
58
+
59
+ Note:
60
+ To validate public internet domains only (e.g., for SSRF protection),
61
+ set both allow_special_use_tlds=False and allow_private_suffixes=False.
62
+
63
+ Returns:
64
+ True if the domain has a valid TLD, False otherwise.
65
+ """
66
+ if "@" in domain:
67
+ return False
68
+
69
+ if "." in domain:
70
+ tld = domain.split(".")[-1].upper()
71
+ if tld in TLDS_ALPHA_BY_DOMAIN:
72
+ return True
73
+ if allow_special_use_tlds and tld in IANA_SPECIAL_USE_TLDS:
74
+ return True
75
+ if allow_private_suffixes and tld in PRIVATE_NETWORK_SUFFIXES:
76
+ return True
77
+
78
+ return False
79
+
80
+
81
+ def is_valid_ip(ip: str) -> bool:
82
+ "Check if an ip is valid"
83
+ parts = ip.split(".")
84
+ if len(parts) == 4:
85
+ for p in parts:
86
+ try:
87
+ if not (0 <= int(p) <= 255):
88
+ return False
89
+ except ValueError:
90
+ return False
91
+
92
+ if int(parts[0]) == 0:
93
+ return False
94
+
95
+ if int(parts[3]) == 0:
96
+ return False
97
+
98
+ return True
99
+
100
+ return False
101
+
102
+
103
+ def is_ip_in_network(ip: str, network: IPv4Network) -> bool:
104
+ "Check if an ip is in a given network"
105
+ if not is_valid_ip(ip):
106
+ return False
107
+
108
+ return ip_address(ip) in network
109
+
110
+
111
+ def is_valid_email(email: str) -> bool:
112
+ "Check if an email is valid"
113
+ parts = email.split("@")
114
+ if len(parts) == 2:
115
+ if is_valid_domain(parts[1]):
116
+ return True
117
+
118
+ return False
119
+
120
+
121
+ def get_hostname() -> str:
122
+ "Get the hostname of the computer howler is running on"
123
+ return socket.gethostname()
124
+
125
+
126
+ def get_mac_address() -> str:
127
+ "Get the mac address of the computer howler is running on"
128
+ return "".join(["{0:02x}".format((uuid.getnode() >> i) & 0xFF) for i in range(0, 8 * 6, 8)][::-1]).upper()