splunk-soar-sdk 3.5.0__tar.gz → 3.6.0__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 (267) hide show
  1. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/PKG-INFO +3 -1
  2. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/pyproject.toml +4 -2
  3. splunk_soar_sdk-3.6.0/release_notes.txt +37 -0
  4. splunk_soar_sdk-3.6.0/release_version.txt +1 -0
  5. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/actions_manager.py +1 -7
  6. splunk_soar_sdk-3.6.0/src/soar_sdk/apis/es/findings.py +27 -0
  7. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/manifests/processors.py +2 -0
  8. splunk_soar_sdk-3.6.0/src/soar_sdk/decorators/on_es_poll.py +187 -0
  9. splunk_soar_sdk-3.6.0/src/soar_sdk/es_client.py +43 -0
  10. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/meta/app.py +1 -0
  11. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/meta/dependencies.py +41 -2
  12. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/models/finding.py +3 -6
  13. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/params.py +9 -2
  14. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_convert_cli.py +4 -4
  15. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_deserializers.py +3 -3
  16. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/conftest.py +8 -1
  17. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/app.json +73 -6
  18. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/pyproject.toml +3 -0
  19. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/src/app.py +15 -18
  20. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/uv.lock +163 -70
  21. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app_with_webhook/app.json +55 -6
  22. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app_with_webhook/pyproject.toml +3 -0
  23. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app_with_webhook/uv.lock +98 -5
  24. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/meta/test_dependencies.py +13 -1
  25. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_app.py +21 -21
  26. splunk_soar_sdk-3.6.0/tests/test_es_client.py +46 -0
  27. splunk_soar_sdk-3.6.0/tests/test_es_on_poll.py +461 -0
  28. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/uv.lock +50 -1
  29. splunk_soar_sdk-3.5.0/release_notes.txt +0 -35
  30. splunk_soar_sdk-3.5.0/release_version.txt +0 -1
  31. splunk_soar_sdk-3.5.0/src/soar_sdk/decorators/on_es_poll.py +0 -215
  32. splunk_soar_sdk-3.5.0/test.png +0 -2
  33. splunk_soar_sdk-3.5.0/test.txt +0 -1
  34. splunk_soar_sdk-3.5.0/tests/test_es_on_poll.py +0 -586
  35. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/ISSUE_TEMPLATE/bug.md +0 -0
  36. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  37. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/pull_request_template.md +0 -0
  38. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/scripts/generate_test_summary.py +0 -0
  39. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/utils/github.js +0 -0
  40. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/utils/update_version.py +0 -0
  41. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/workflows/code_quality.yml +0 -0
  42. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/workflows/commit_hygiene.yml +0 -0
  43. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/workflows/generate_docs.yml +0 -0
  44. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/workflows/integration_tests.yml +0 -0
  45. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.github/workflows/semantic_release.yml +0 -0
  46. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.gitignore +0 -0
  47. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.pre-commit-config.yaml +0 -0
  48. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/.releaserc +0 -0
  49. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/LICENSE +0 -0
  50. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/README.md +0 -0
  51. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/commitlint.config.js +0 -0
  52. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/api_reference.rst +0 -0
  53. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/app_structure/index.rst +0 -0
  54. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/app_structure/pre-commit-config.yaml.rst +0 -0
  55. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/app_structure/pyproject.toml.rst +0 -0
  56. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/app_structure/src_app.rst +0 -0
  57. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/changelog.rst +0 -0
  58. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/cli_reference.rst +0 -0
  59. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/conf.py +0 -0
  60. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/custom_views/index.rst +0 -0
  61. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/custom_views/reusable_components.md +0 -0
  62. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/custom_views/templates.md +0 -0
  63. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/custom_views/view_handlers.md +0 -0
  64. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/getting_started/defining_asset.rst +0 -0
  65. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/getting_started/first_action.rst +0 -0
  66. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/getting_started/index.rst +0 -0
  67. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/getting_started/init_app.rst +0 -0
  68. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/getting_started/installation.rst +0 -0
  69. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/getting_started/testing_and_building.rst +0 -0
  70. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/docs/index.rst +0 -0
  71. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/README.md +0 -0
  72. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/install.sh +0 -0
  73. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/mcp_config.json +0 -0
  74. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/pyproject.toml +0 -0
  75. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/pytest.ini +0 -0
  76. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/src/soar_test_assistant/__init__.py +0 -0
  77. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/src/soar_test_assistant/server.py +0 -0
  78. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/tests/__init__.py +0 -0
  79. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/tests/test_analyzer.py +0 -0
  80. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/mcp_server/uv.lock +0 -0
  81. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/__init__.py +0 -0
  82. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/abstract.py +0 -0
  83. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/action_results.py +0 -0
  84. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/apis/__init__.py +0 -0
  85. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/apis/artifact.py +0 -0
  86. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/apis/container.py +0 -0
  87. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/apis/utils.py +0 -0
  88. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/apis/vault.py +0 -0
  89. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/app.py +0 -0
  90. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/app_cli_runner.py +0 -0
  91. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/app_client.py +0 -0
  92. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/app_templates/basic_app/.gitignore +0 -0
  93. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/app_templates/basic_app/.pre-commit-config.yaml +0 -0
  94. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/app_templates/basic_app/logo.svg +0 -0
  95. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/app_templates/basic_app/logo_dark.svg +0 -0
  96. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/app_templates/basic_app/src/__init__.py +0 -0
  97. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/app_templates/basic_app/uv.lock +0 -0
  98. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/asset.py +0 -0
  99. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/asset_state.py +0 -0
  100. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/async_utils.py +0 -0
  101. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/__init__.py +0 -0
  102. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/cli.py +0 -0
  103. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/init/__init__.py +0 -0
  104. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/init/cli.py +0 -0
  105. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/manifests/__init__.py +0 -0
  106. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/manifests/cli.py +0 -0
  107. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/manifests/deserializers.py +0 -0
  108. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/manifests/serializers.py +0 -0
  109. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/package/cli.py +0 -0
  110. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/package/utils.py +0 -0
  111. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/path_utils.py +0 -0
  112. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/test/__init__.py +0 -0
  113. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/test/cli.py +0 -0
  114. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/cli/utils.py +0 -0
  115. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/code_renderers/__init__.py +0 -0
  116. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/code_renderers/action_renderer.py +0 -0
  117. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/code_renderers/app_renderer.py +0 -0
  118. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/code_renderers/asset_renderer.py +0 -0
  119. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/code_renderers/renderer.py +0 -0
  120. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/code_renderers/templates/pyproject.toml.jinja +0 -0
  121. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/code_renderers/toml_renderer.py +0 -0
  122. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/colors.py +0 -0
  123. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/compat.py +0 -0
  124. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/crypto.py +0 -0
  125. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/decorators/__init__.py +0 -0
  126. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/decorators/action.py +0 -0
  127. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/decorators/make_request.py +0 -0
  128. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/decorators/on_poll.py +0 -0
  129. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/decorators/test_connectivity.py +0 -0
  130. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/decorators/view_handler.py +0 -0
  131. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/decorators/webhook.py +0 -0
  132. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/exceptions.py +0 -0
  133. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/extras/__init__.py +0 -0
  134. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/extras/email/__init__.py +0 -0
  135. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/extras/email/processor.py +0 -0
  136. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/extras/email/rfc5322.py +0 -0
  137. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/extras/email/utils.py +0 -0
  138. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/field_utils.py +0 -0
  139. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/input_spec.py +0 -0
  140. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/logging.py +0 -0
  141. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/meta/__init__.py +0 -0
  142. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/meta/actions.py +0 -0
  143. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/meta/adapters.py +0 -0
  144. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/meta/datatypes.py +0 -0
  145. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/meta/webhooks.py +0 -0
  146. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/models/__init__.py +0 -0
  147. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/models/artifact.py +0 -0
  148. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/models/attachment_input.py +0 -0
  149. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/models/container.py +0 -0
  150. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/models/vault_attachment.py +0 -0
  151. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/models/view.py +0 -0
  152. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/paths.py +0 -0
  153. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/py.typed +0 -0
  154. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/action_result.py +0 -0
  155. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/app.py +0 -0
  156. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/base_connector.py +0 -0
  157. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/connector_result.py +0 -0
  158. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/consts.py +0 -0
  159. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/encryption_helper.py +0 -0
  160. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/install_info.py +0 -0
  161. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/json_keys.py +0 -0
  162. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/ph_ipc.py +0 -0
  163. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom/vault.py +0 -0
  164. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom_common/app_interface/app_interface.py +0 -0
  165. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/shims/phantom_common/encryption/encryption_manager_factory.py +0 -0
  166. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/templates/base/base_template.html +0 -0
  167. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/templates/base/error.html +0 -0
  168. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/templates/base/header.html +0 -0
  169. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/templates/base/logo_header.html +0 -0
  170. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/templates/components/pie_chart.html +0 -0
  171. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/templates/widgets/widget_resize_snippet.html +0 -0
  172. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/templates/widgets/widget_template.html +0 -0
  173. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/types.py +0 -0
  174. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/views/__init__.py +0 -0
  175. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/views/component_registry.py +0 -0
  176. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/views/components/__init__.py +0 -0
  177. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/views/components/pie_chart.py +0 -0
  178. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/views/template_filters.py +0 -0
  179. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/views/template_renderer.py +0 -0
  180. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/views/view_parser.py +0 -0
  181. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/webhooks/__init__.py +0 -0
  182. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/webhooks/models.py +0 -0
  183. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/src/soar_sdk/webhooks/routing.py +0 -0
  184. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/__init__.py +0 -0
  185. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/__init__.py +0 -0
  186. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/datapath_parse.py +0 -0
  187. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/manifests/__init__.py +0 -0
  188. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/manifests/test_processors.py +0 -0
  189. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/manifests/test_python_version_resolution.py +0 -0
  190. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_assets/converted_app/actions.py.txt +0 -0
  191. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_cli.py +0 -0
  192. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_init_cli.py +0 -0
  193. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_manifests_cli.py +0 -0
  194. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_package_cli.py +0 -0
  195. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_serializers.py +0 -0
  196. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_test_cli.py +0 -0
  197. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/cli/test_utils.py +0 -0
  198. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/code_renderers/test_action_renderer.py +0 -0
  199. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/example_asset.json +0 -0
  200. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/logo.svg +0 -0
  201. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/logo_dark.svg +0 -0
  202. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/release_notes/v1.md +0 -0
  203. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/src/__init__.py +0 -0
  204. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/src/actions/__init__.py +0 -0
  205. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/src/actions/async_action.py +0 -0
  206. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/src/actions/generate_category.py +0 -0
  207. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/src/actions/reverse_string.py +0 -0
  208. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/src/ignoreme.txt +0 -0
  209. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app/templates/reverse_string.html +0 -0
  210. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app_with_webhook/example_asset.json +0 -0
  211. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app_with_webhook/logo.svg +0 -0
  212. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app_with_webhook/logo_dark.svg +0 -0
  213. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app_with_webhook/src/__init__.py +0 -0
  214. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/example_app_with_webhook/src/app.py +0 -0
  215. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/integration/__init__.py +0 -0
  216. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/integration/conftest.py +0 -0
  217. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/integration/phantom_constants.py +0 -0
  218. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/integration/phantom_instance.py +0 -0
  219. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/integration/soar_client.py +0 -0
  220. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/integration/test_example_app.py +0 -0
  221. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/integration/test_example_app_with_webhook.py +0 -0
  222. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/interfaces/__init__.py +0 -0
  223. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/interfaces/test_artifact_interface.py +0 -0
  224. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/interfaces/test_container_interface.py +0 -0
  225. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/interfaces/test_vault_interface.py +0 -0
  226. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/meta/__init__.py +0 -0
  227. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/meta/test_actions.py +0 -0
  228. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/meta/test_adapters.py +0 -0
  229. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/meta/test_datatypes.py +0 -0
  230. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/meta/test_webhooks.py +0 -0
  231. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/mocks/__init__.py +0 -0
  232. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/mocks/dynamic_mocks.py +0 -0
  233. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/mocks/importable_action.py +0 -0
  234. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/stubs.py +0 -0
  235. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_action_results.py +0 -0
  236. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_actions_manager.py +0 -0
  237. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_app_action.py +0 -0
  238. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_app_action_params.py +0 -0
  239. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_app_action_results.py +0 -0
  240. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_app_client.py +0 -0
  241. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_app_runner.py +0 -0
  242. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_asset.py +0 -0
  243. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_asset_state.py +0 -0
  244. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_assets/splunk-sdk-2.1.0.tar.gz +0 -0
  245. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_async_integration.py +0 -0
  246. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_async_utils.py +0 -0
  247. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_attachment_input.py +0 -0
  248. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_code_renderers.py +0 -0
  249. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_compat.py +0 -0
  250. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_container.py +0 -0
  251. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_custom_views.py +0 -0
  252. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_email_processor.py +0 -0
  253. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_encryption.py +0 -0
  254. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_field_utils.py +0 -0
  255. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_finding.py +0 -0
  256. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_input_spec.py +0 -0
  257. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_logging.py +0 -0
  258. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_make_request_action.py +0 -0
  259. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_on_poll.py +0 -0
  260. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_params.py +0 -0
  261. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_rfc5322.py +0 -0
  262. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_template_filters.py +0 -0
  263. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_template_renderer.py +0 -0
  264. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_test_connectivity.py +0 -0
  265. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/test_view_parser.py +0 -0
  266. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/webhooks/test_models.py +0 -0
  267. {splunk_soar_sdk-3.5.0 → splunk_soar_sdk-3.6.0}/tests/webhooks/test_routing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splunk-soar-sdk
3
- Version: 3.5.0
3
+ Version: 3.6.0
4
4
  Summary: The official framework for developing and testing Splunk SOAR Apps
5
5
  Project-URL: Homepage, https://github.com/phantomcyber/splunk-soar-sdk
6
6
  Project-URL: Documentation, https://github.com/phantomcyber/splunk-soar-sdk
@@ -22,6 +22,8 @@ Requires-Dist: bleach>=6.2.0
22
22
  Requires-Dist: build>=1.3.0
23
23
  Requires-Dist: click<8.2.0,>=8.0.0
24
24
  Requires-Dist: distro>=1.8.0
25
+ Requires-Dist: hatchling>=1.28.0
26
+ Requires-Dist: httpx-retries>=0.4.5
25
27
  Requires-Dist: httpx>=0.28.1
26
28
  Requires-Dist: humanize>=4.12.2
27
29
  Requires-Dist: jinja2>=3.1.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "splunk-soar-sdk"
3
- version = "3.5.0"
3
+ version = "3.6.0"
4
4
  description = "The official framework for developing and testing Splunk SOAR Apps"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13, <3.15"
@@ -44,6 +44,8 @@ dependencies = [
44
44
  "packaging>=25.0",
45
45
  "build>=1.3.0",
46
46
  "setuptools>=80.9.0",
47
+ "httpx-retries>=0.4.5",
48
+ "hatchling>=1.28.0",
47
49
  ]
48
50
 
49
51
  [project.urls]
@@ -243,4 +245,4 @@ match = "(?!test_).*\\.py"
243
245
  match-dir = "(?!tests|__pycache__|build|dist).*"
244
246
 
245
247
  [tool.codespell]
246
- ignore-words-list = "MergeT"
248
+ ignore-words-list = "MergeT,asend"
@@ -0,0 +1,37 @@
1
+ # [3.6.0](https://github.com/phantomcyber/splunk-soar-sdk/compare/3.5.0...3.6.0) (2025-12-12)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * remove the useless test files from the root of the repo ([f529497](https://github.com/phantomcyber/splunk-soar-sdk/commit/f529497a397a9879dbe5e264a2b562916b6bbcf8))
7
+
8
+
9
+ ### Features
10
+
11
+ * add `supports_es_polling` field to manifest ([0eb75dd](https://github.com/phantomcyber/splunk-soar-sdk/commit/0eb75dd56a7fb512fff0533bdb64317d4de776d6))
12
+ * add a client for submitting ES findings ([23bf0b7](https://github.com/phantomcyber/splunk-soar-sdk/commit/23bf0b7bea5e9437bffecfe783a20abf9c4eb35b))
13
+ * enable building the SDK from a local directory instead of fetching wheel ([8ca7a6c](https://github.com/phantomcyber/splunk-soar-sdk/commit/8ca7a6c007b740c56db2407306a32363d203da76))
14
+ * update ES polling to use the new workflow ([d69784c](https://github.com/phantomcyber/splunk-soar-sdk/commit/d69784c462eccab6f5a0796cc9573cff5e816a1c))
15
+
16
+
17
+
18
+
19
+
20
+ # [3.6.0](https://github.com/phantomcyber/splunk-soar-sdk/compare/3.5.0...3.6.0) (2025-12-12)
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * remove the useless test files from the root of the repo ([f529497](https://github.com/phantomcyber/splunk-soar-sdk/commit/f529497a397a9879dbe5e264a2b562916b6bbcf8))
26
+
27
+
28
+ ### Features
29
+
30
+ * add `supports_es_polling` field to manifest ([0eb75dd](https://github.com/phantomcyber/splunk-soar-sdk/commit/0eb75dd56a7fb512fff0533bdb64317d4de776d6))
31
+ * add a client for submitting ES findings ([23bf0b7](https://github.com/phantomcyber/splunk-soar-sdk/commit/23bf0b7bea5e9437bffecfe783a20abf9c4eb35b))
32
+ * enable building the SDK from a local directory instead of fetching wheel ([8ca7a6c](https://github.com/phantomcyber/splunk-soar-sdk/commit/8ca7a6c007b740c56db2407306a32363d203da76))
33
+ * update ES polling to use the new workflow ([d69784c](https://github.com/phantomcyber/splunk-soar-sdk/commit/d69784c462eccab6f5a0796cc9573cff5e816a1c))
34
+
35
+
36
+
37
+
@@ -0,0 +1 @@
1
+ 3.6.0
@@ -24,6 +24,7 @@ class ActionsManager(BaseConnector):
24
24
 
25
25
  self._actions: dict[str, Action] = {}
26
26
  self.__app_dir: Path | None = None
27
+ self.supports_es_polling: bool = False
27
28
 
28
29
  def get_action(self, identifier: str) -> Action | None:
29
30
  """Convenience method for getting an Action callable from its identifier.
@@ -123,13 +124,6 @@ class ActionsManager(BaseConnector):
123
124
  # For non-broker just proceed as we did before
124
125
  return super().get_app_dir()
125
126
 
126
- def send_finding_to_es(self, finding: dict[str, Any]) -> str:
127
- """Send finding to ES.
128
-
129
- Returns finding_id: ID for the finding in ES.
130
- """
131
- return ""
132
-
133
127
  @classmethod
134
128
  def get_soar_base_url(cls) -> str:
135
129
  """Get the base URL of the Splunk SOAR instance this app is running on."""
@@ -0,0 +1,27 @@
1
+ import httpx
2
+ from pydantic import Field
3
+
4
+ from soar_sdk.models.finding import Finding
5
+
6
+
7
+ class CreateFindingResponse(Finding):
8
+ """The return type from creating a Finding."""
9
+
10
+ time: str = Field(alias="_time")
11
+ finding_id: str
12
+
13
+
14
+ class Findings:
15
+ """Client for ES Findings API."""
16
+
17
+ def __init__(self, client: httpx.Client) -> None:
18
+ self._client = client
19
+
20
+ def create(self, finding: Finding) -> CreateFindingResponse:
21
+ """Create a new Finding."""
22
+ res = self._client.post(
23
+ "/services/public/v2/findings",
24
+ data=finding.model_dump(),
25
+ )
26
+ res.raise_for_status()
27
+ return CreateFindingResponse(**res.json())
@@ -74,6 +74,8 @@ class ManifestProcessor:
74
74
  f"{module_name}.{app_instance_name}.handle_webhook"
75
75
  )
76
76
 
77
+ app_meta.supports_es_polling = app.actions_manager.supports_es_polling
78
+
77
79
  return app_meta
78
80
 
79
81
  def create(self) -> None:
@@ -0,0 +1,187 @@
1
+ import asyncio
2
+ import inspect
3
+ from collections.abc import Callable
4
+ from functools import wraps
5
+ from typing import TYPE_CHECKING, Any, get_args
6
+
7
+ from pydantic import ValidationError
8
+
9
+ from soar_sdk.abstract import SOARClient
10
+ from soar_sdk.action_results import ActionResult
11
+ from soar_sdk.es_client import ESClient
12
+ from soar_sdk.exceptions import ActionFailure
13
+ from soar_sdk.logging import getLogger
14
+ from soar_sdk.meta.actions import ActionMeta
15
+ from soar_sdk.models.container import Container
16
+ from soar_sdk.models.finding import Finding
17
+ from soar_sdk.params import OnESPollParams
18
+ from soar_sdk.types import Action, action_protocol
19
+
20
+ if TYPE_CHECKING:
21
+ from soar_sdk.app import App
22
+
23
+
24
+ ESPollingYieldType = Finding
25
+ ESPollingSendType = int | None
26
+
27
+
28
+ class OnESPollDecorator:
29
+ """Class-based decorator for tagging a function as the special 'on es poll' action."""
30
+
31
+ def __init__(self, app: "App") -> None:
32
+ self.app = app
33
+
34
+ def __call__(self, function: Callable) -> Action:
35
+ """Decorator for the 'on es poll' action. The decorated function must be a Generator or AsyncGenerator. Only one on_es_poll action is allowed per app.
36
+
37
+ Usage:
38
+ The generator should yield a `Finding`. Upon receiving an event from the generator, the SDK will submit the Finding to Splunk Enterprise Security and create a linked SOAR Container.
39
+ The generator should accept a "send type" of `int | None`. When a Finding is successfully delivered to ES and linked to a Container, the SDK will send the Container ID back into the generator. The Container is useful for storing large attachments included with the Finding.
40
+ If the Finding cannot be successfully delivered to ES, the SDK will stop polling and return a failed result for the action run.
41
+ """
42
+ if self.app.actions_manager.get_action("on_es_poll"):
43
+ raise TypeError(
44
+ "The 'on_es_poll' decorator can only be used once per App instance."
45
+ )
46
+
47
+ is_generator = inspect.isgeneratorfunction(function)
48
+ is_async_generator = inspect.isasyncgenfunction(function)
49
+
50
+ generator_type = inspect.signature(function).return_annotation
51
+ generator_type_args = get_args(generator_type)
52
+
53
+ if not (is_generator or is_async_generator) or len(generator_type_args) < 2:
54
+ raise TypeError(
55
+ "The on_es_poll function must be a Generator or AsyncGenerator (use 'yield')."
56
+ )
57
+
58
+ yield_type = generator_type_args[0]
59
+ send_type = generator_type_args[1]
60
+
61
+ if yield_type != ESPollingYieldType:
62
+ raise TypeError(
63
+ f"@on_es_poll generator should have yield type {ESPollingYieldType}."
64
+ )
65
+ if send_type != ESPollingSendType:
66
+ raise TypeError(
67
+ f"@on_es_poll generator should have send type {ESPollingSendType}."
68
+ )
69
+
70
+ action_identifier = "on_es_poll"
71
+ action_name = "on es poll"
72
+
73
+ validated_params_class = OnESPollParams
74
+ logger = getLogger()
75
+
76
+ @action_protocol
77
+ @wraps(function)
78
+ def inner(
79
+ params: OnESPollParams,
80
+ soar: SOARClient = self.app.soar_client,
81
+ *args: Any, # noqa: ANN401
82
+ **kwargs: Any, # noqa: ANN401
83
+ ) -> bool:
84
+ try:
85
+ action_params = validated_params_class.model_validate(params)
86
+ except ValidationError as e:
87
+ logger.info(f"Parameter validation error: {e!s}")
88
+ return self.app._adapt_action_result(
89
+ ActionResult(status=False, message=f"Invalid parameters: {e!s}"),
90
+ self.app.actions_manager,
91
+ )
92
+ es = ESClient(params.es_base_url, params.es_session_key)
93
+ kwargs = self.app._build_magic_args(function, soar=soar, **kwargs)
94
+ generator = function(action_params, *args, **kwargs)
95
+
96
+ if is_async_generator:
97
+
98
+ def polling_step(
99
+ last_container_id: ESPollingSendType,
100
+ ) -> ESPollingYieldType:
101
+ return asyncio.run(generator.asend(last_container_id))
102
+ else:
103
+
104
+ def polling_step(
105
+ last_container_id: ESPollingSendType,
106
+ ) -> ESPollingYieldType:
107
+ return generator.send(last_container_id)
108
+
109
+ last_container_id = None
110
+ while True:
111
+ try:
112
+ item = polling_step(last_container_id)
113
+ except (StopIteration, StopAsyncIteration):
114
+ return self.app._adapt_action_result(
115
+ ActionResult(
116
+ status=True, message="Finding processing complete"
117
+ ),
118
+ self.app.actions_manager,
119
+ )
120
+ except ActionFailure as e:
121
+ e.set_action_name(action_name)
122
+ return self.app._adapt_action_result(
123
+ ActionResult(status=False, message=str(e)),
124
+ self.app.actions_manager,
125
+ )
126
+ except Exception as e:
127
+ self.app.actions_manager.add_exception(e)
128
+ logger.info(f"Error during finding processing: {e!s}")
129
+ return self.app._adapt_action_result(
130
+ ActionResult(status=False, message=str(e)),
131
+ self.app.actions_manager,
132
+ )
133
+
134
+ if type(item) is not ESPollingYieldType:
135
+ logger.info(
136
+ f"Warning: expected {ESPollingYieldType}, got {type(item)}, skipping"
137
+ )
138
+ continue
139
+ finding = es.findings.create(item)
140
+ logger.info(f"Created finding {finding.finding_id}")
141
+
142
+ container = Container(
143
+ name=finding.rule_title,
144
+ description=finding.rule_description,
145
+ severity=finding.urgency or "medium",
146
+ status=finding.status,
147
+ owner_id=finding.owner,
148
+ sensitivity=finding.disposition,
149
+ tags=finding.source,
150
+ external_id=finding.finding_id,
151
+ data={
152
+ "security_domain": finding.security_domain,
153
+ "risk_score": finding.risk_score,
154
+ "risk_object": finding.risk_object,
155
+ "risk_object_type": finding.risk_object_type,
156
+ },
157
+ )
158
+ ret_val, message, last_container_id = (
159
+ self.app.actions_manager.save_container(container.to_dict())
160
+ )
161
+ logger.info(f"Creating container for finding: {finding.rule_title}")
162
+ if not ret_val:
163
+ raise ActionFailure(f"Failed to create container: {message}")
164
+
165
+ inner.params_class = validated_params_class
166
+
167
+ class OnESPollActionMeta(ActionMeta):
168
+ def model_dump(self, *args: object, **kwargs: object) -> dict[str, Any]:
169
+ data = super().model_dump(*args, **kwargs)
170
+ data["output"] = []
171
+ return data
172
+
173
+ inner.meta = OnESPollActionMeta(
174
+ action=action_name,
175
+ identifier=action_identifier,
176
+ description=inspect.getdoc(function) or action_name,
177
+ verbose="Callback action for the on_es_poll ingest functionality",
178
+ type="ingest",
179
+ read_only=True,
180
+ parameters=validated_params_class,
181
+ versions="EQ(*)",
182
+ )
183
+
184
+ self.app.actions_manager.set_action(action_identifier, inner)
185
+ self.app.actions_manager.supports_es_polling = True
186
+ self.app._dev_skip_in_pytest(function, inner)
187
+ return inner
@@ -0,0 +1,43 @@
1
+ import httpx
2
+ from httpx_retries import Retry, RetryTransport
3
+ from httpx_retries.retry import HTTPMethod, HTTPStatus
4
+
5
+ from soar_sdk.apis.es.findings import Findings
6
+
7
+ RETRYABLE_METHODS = [
8
+ HTTPMethod.GET,
9
+ HTTPMethod.PUT,
10
+ HTTPMethod.POST,
11
+ HTTPMethod.PATCH,
12
+ HTTPMethod.DELETE,
13
+ ]
14
+ RETRYABLE_STATUSES = [
15
+ HTTPStatus.TOO_MANY_REQUESTS,
16
+ HTTPStatus.BAD_GATEWAY,
17
+ HTTPStatus.SERVICE_UNAVAILABLE,
18
+ HTTPStatus.GATEWAY_TIMEOUT,
19
+ ]
20
+
21
+
22
+ class ESClient:
23
+ """A client for accessing Splunk Enterprise Security APIs."""
24
+
25
+ def __init__(self, base_url: str, session_key: str, verify: bool = True) -> None:
26
+ transport = RetryTransport(
27
+ transport=httpx.HTTPTransport(verify=verify),
28
+ retry=Retry(
29
+ allowed_methods=RETRYABLE_METHODS,
30
+ status_forcelist=RETRYABLE_STATUSES,
31
+ total=5,
32
+ ),
33
+ )
34
+ self._client = httpx.Client(
35
+ base_url=base_url,
36
+ transport=transport,
37
+ headers={"Authorization": f"Splunk {session_key}"},
38
+ )
39
+
40
+ @property
41
+ def findings(self) -> Findings:
42
+ """The ES /public/v2/findings API."""
43
+ return Findings(self._client)
@@ -45,6 +45,7 @@ class AppMeta(BaseModel):
45
45
  pip314_dependencies: DependencyList = Field(default_factory=DependencyList)
46
46
 
47
47
  webhook: WebhookMeta | None = None
48
+ supports_es_polling: bool = False
48
49
 
49
50
  @field_validator("python_version", mode="before")
50
51
  @classmethod
@@ -52,7 +52,8 @@ remove_when_soar_newer_than(
52
52
  "If the Splunk SDK is available as a wheel now, remove it, and remove all of the code for building wheels from source.",
53
53
  )
54
54
  DEPENDENCIES_TO_BUILD = {
55
- "splunk_sdk", # https://github.com/splunk/splunk-sdk-python/pull/656
55
+ "splunk_sdk", # https://github.com/splunk/splunk-sdk-python/pull/656,
56
+ "splunk_soar_sdk", # Useful to build from source when developing the SDK
56
57
  }
57
58
 
58
59
 
@@ -189,6 +190,23 @@ class UvSourceDistribution(BaseModel):
189
190
  return Path(wheel_path).name, f.read()
190
191
 
191
192
 
193
+ class UvSourceDirectory(BaseModel):
194
+ """Represents a Python dependency to be built from a source directory on the local filesystem."""
195
+
196
+ directory: str
197
+
198
+ def build(self) -> tuple[str, bytes]:
199
+ """Build a wheel from a local source directory."""
200
+ with TemporaryDirectory() as build_dir:
201
+ builder = build.ProjectBuilder(
202
+ self.directory,
203
+ runner=UvSourceDistribution._builder_runner,
204
+ )
205
+ wheel_path = builder.build("wheel", build_dir)
206
+ with open(wheel_path, "rb") as f:
207
+ return Path(wheel_path).name, f.read()
208
+
209
+
192
210
  class DependencyWheel(BaseModel):
193
211
  """Represents a Python package dependency with all the information required to fetch its wheel(s) from the CDN."""
194
212
 
@@ -199,6 +217,7 @@ class DependencyWheel(BaseModel):
199
217
  wheel: UvWheel | None = Field(exclude=True, default=None)
200
218
  wheel_aarch64: UvWheel | None = Field(exclude=True, default=None)
201
219
  sdist: UvSourceDistribution | None = Field(exclude=True, default=None)
220
+ source_dir: UvSourceDirectory | None = Field(exclude=True, default=None)
202
221
 
203
222
  async def collect_wheels(self) -> AsyncGenerator[tuple[str, bytes]]:
204
223
  """Collect a list of wheel files to fetch for this dependency across all platforms."""
@@ -208,6 +227,12 @@ class DependencyWheel(BaseModel):
208
227
  yield (f"wheels/shared/{wheel_name}", wheel_bytes)
209
228
  return
210
229
 
230
+ if self.wheel is None and self.source_dir is not None:
231
+ logger.info(f"Building local sources for {self.input_file}")
232
+ wheel_name, wheel_bytes = self.source_dir.build()
233
+ yield (f"wheels/shared/{wheel_name}", wheel_bytes)
234
+ return
235
+
211
236
  if self.wheel is None:
212
237
  raise ValueError(
213
238
  f"Could not find a suitable wheel or source distribution for {self.module} in uv.lock"
@@ -247,6 +272,13 @@ class UvDependency(BaseModel):
247
272
  name: str
248
273
 
249
274
 
275
+ class UvSource(BaseModel):
276
+ """Represents the source of a Python package in the uv lock."""
277
+
278
+ registry: str | None = None
279
+ directory: str | None = None
280
+
281
+
250
282
  class UvPackage(BaseModel):
251
283
  """Represents a Python package loaded from the uv lock."""
252
284
 
@@ -258,6 +290,7 @@ class UvPackage(BaseModel):
258
290
  )
259
291
  wheels: list[UvWheel] = []
260
292
  sdist: UvSourceDistribution | None = None
293
+ source: UvSource
261
294
 
262
295
  def _find_wheel(
263
296
  self,
@@ -366,6 +399,12 @@ class UvPackage(BaseModel):
366
399
  ):
367
400
  wheel.sdist = self.sdist
368
401
 
402
+ if (
403
+ self.source.directory is not None
404
+ and UvLock.normalize_package_name(self.name) in DEPENDENCIES_TO_BUILD
405
+ ):
406
+ wheel.source_dir = UvSourceDirectory(directory=self.source.directory)
407
+
369
408
  try:
370
409
  wheel_x86_64 = self._find_wheel(
371
410
  abi_precedence, python_precedence, self.platform_precedence_x86_64
@@ -373,7 +412,7 @@ class UvPackage(BaseModel):
373
412
  wheel.input_file = f"{wheel_x86_64.basename}.whl"
374
413
  wheel.wheel = wheel_x86_64
375
414
  except FileNotFoundError as e:
376
- if wheel.sdist is None:
415
+ if wheel.sdist is None and wheel.source_dir is None:
377
416
  raise FileNotFoundError(
378
417
  f"Could not find a suitable x86_64 wheel or source distribution for {self.name}"
379
418
  ) from e
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from pydantic import BaseModel
3
+ from pydantic import BaseModel, ConfigDict
4
4
 
5
5
 
6
6
  class DrilldownSearch(BaseModel):
@@ -27,10 +27,7 @@ class Finding(BaseModel):
27
27
  for investigation workflow.
28
28
  """
29
29
 
30
- class Config:
31
- """Pydantic config."""
32
-
33
- extra = "forbid"
30
+ model_config = ConfigDict(extra="forbid")
34
31
 
35
32
  rule_title: str
36
33
  rule_description: str
@@ -52,4 +49,4 @@ class Finding(BaseModel):
52
49
 
53
50
  def to_dict(self) -> dict[str, Any]:
54
51
  """Convert the finding to a dictionary."""
55
- return self.dict(exclude_none=True)
52
+ return self.model_dump(exclude_none=True)
@@ -207,17 +207,24 @@ class OnESPollParams(Params):
207
207
  description="Start of time range, in epoch time (milliseconds).",
208
208
  required=False,
209
209
  )
210
-
211
210
  end_time: int = Param(
212
211
  description="End of time range, in epoch time (milliseconds).",
213
212
  required=False,
214
213
  )
215
-
216
214
  container_count: int = Param(
217
215
  description="Maximum number of container records to query for.",
218
216
  required=False,
219
217
  )
220
218
 
219
+ es_base_url: str = Param(
220
+ description="Base URL for the Splunk Enterprise Security API",
221
+ required=True,
222
+ )
223
+ es_session_key: str = Param(
224
+ description="Session token for the Splunk Enterprise Security API",
225
+ required=True,
226
+ )
227
+
221
228
 
222
229
  class MakeRequestParams(Params):
223
230
  """Canonical parameters for the special make request action."""
@@ -158,7 +158,7 @@ def test_convert_cli(runner, tmp_path, app_meta):
158
158
 
159
159
  app_dir = tmp_path / "test_app"
160
160
  app_dir.mkdir()
161
- (app_dir / "app.json").write_text(app_meta.json())
161
+ (app_dir / "app.json").write_text(app_meta.model_dump_json())
162
162
  (app_dir / app_meta.logo).touch()
163
163
  (app_dir / app_meta.logo_dark).touch()
164
164
 
@@ -198,11 +198,11 @@ def test_convert_cli_updates_py_versions(runner, tmp_path, app_meta):
198
198
  )
199
199
  }
200
200
 
201
- app_meta.python_version = [PythonVersion.PY_3_13]
201
+ app_meta.python_version = PythonVersion.PY_3_13
202
202
 
203
203
  app_dir = tmp_path / "test_app"
204
204
  app_dir.mkdir()
205
- (app_dir / "app.json").write_text(app_meta.json())
205
+ (app_dir / "app.json").write_text(app_meta.model_dump_json())
206
206
  (app_dir / app_meta.logo).touch()
207
207
  (app_dir / app_meta.logo_dark).touch()
208
208
 
@@ -244,7 +244,7 @@ def test_convert_cli_with_default_output(runner, tmp_path, app_meta):
244
244
 
245
245
  app_dir = tmp_path / "test_app"
246
246
  app_dir.mkdir()
247
- (app_dir / "app.json").write_text(app_meta.json())
247
+ (app_dir / "app.json").write_text(app_meta.model_dump_json())
248
248
  (app_dir / app_meta.logo).touch()
249
249
  (app_dir / app_meta.logo_dark).touch()
250
250
 
@@ -574,7 +574,7 @@ def test_action_deserializer_with_underscored_params():
574
574
  "test_action", {"_underscore": {"data_type": "string"}}
575
575
  )
576
576
 
577
- field = result.__fields__["underscore"]
577
+ field = result.model_fields["underscore"]
578
578
  assert field.alias == "_underscore"
579
579
 
580
580
 
@@ -717,8 +717,8 @@ def test_parse_parameters_with_complex_params():
717
717
  assert "param3" in result.__annotations__
718
718
  assert "param4" not in result.__annotations__
719
719
 
720
- assert "param5" in result.__fields__
721
- assert result.__fields__["param5"].alias == "_param5"
720
+ assert "param5" in result.model_fields
721
+ assert result.model_fields["param5"].alias == "_param5"
722
722
 
723
723
 
724
724
  def test_complex_output_specification():
@@ -22,6 +22,7 @@ from soar_sdk.meta.dependencies import (
22
22
  UvDependency,
23
23
  UvLock,
24
24
  UvPackage,
25
+ UvSource,
25
26
  UvSourceDistribution,
26
27
  UvWheel,
27
28
  )
@@ -308,8 +309,14 @@ def fake_uv_lockfile(fake_wheel) -> UvLock:
308
309
  dependencies=[
309
310
  UvDependency(name="fakepkg"),
310
311
  ],
312
+ source=UvSource(registry="https://pypi.python.org/simple"),
313
+ ),
314
+ UvPackage(
315
+ name="fakepkg",
316
+ version="1.0.0",
317
+ wheels=[fake_wheel],
318
+ source=UvSource(registry="https://pypi.python.org/simple"),
311
319
  ),
312
- UvPackage(name="fakepkg", version="1.0.0", wheels=[fake_wheel]),
313
320
  ]
314
321
  )
315
322