splunk-soar-sdk 3.1.0__tar.gz → 3.2.1__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 (232) hide show
  1. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/PKG-INFO +1 -1
  2. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/pyproject.toml +1 -1
  3. splunk_soar_sdk-3.2.1/release_notes.txt +21 -0
  4. splunk_soar_sdk-3.2.1/release_version.txt +1 -0
  5. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/actions_manager.py +7 -0
  6. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/app.py +26 -0
  7. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/code_renderers/action_renderer.py +56 -0
  8. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/code_renderers/app_renderer.py +11 -0
  9. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/decorators/__init__.py +2 -0
  10. splunk_soar_sdk-3.2.1/src/soar_sdk/decorators/on_es_poll.py +218 -0
  11. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/meta/dependencies.py +49 -3
  12. splunk_soar_sdk-3.2.1/src/soar_sdk/models/__init__.py +15 -0
  13. splunk_soar_sdk-3.2.1/src/soar_sdk/models/attachment_input.py +27 -0
  14. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/models/container.py +1 -0
  15. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/params.py +19 -0
  16. splunk_soar_sdk-3.2.1/tests/example_app/app.json +1299 -0
  17. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/src/app.py +74 -1
  18. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/uv.lock +66 -34
  19. splunk_soar_sdk-3.2.1/tests/example_app_with_webhook/app.json +598 -0
  20. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app_with_webhook/uv.lock +100 -34
  21. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/meta/test_dependencies.py +38 -0
  22. splunk_soar_sdk-3.2.1/tests/test_attachment_input.py +54 -0
  23. splunk_soar_sdk-3.2.1/tests/test_es_on_poll.py +586 -0
  24. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/uv.lock +1 -1
  25. splunk_soar_sdk-3.1.0/release_notes.txt +0 -21
  26. splunk_soar_sdk-3.1.0/release_version.txt +0 -1
  27. splunk_soar_sdk-3.1.0/tests/example_app/app.json +0 -1250
  28. splunk_soar_sdk-3.1.0/tests/example_app_with_webhook/app.json +0 -558
  29. splunk_soar_sdk-3.1.0/tests/mocks/__init__.py +0 -0
  30. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.github/ISSUE_TEMPLATE/bug.md +0 -0
  31. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  32. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.github/pull_request_template.md +0 -0
  33. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.github/utils/github.js +0 -0
  34. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.github/utils/update_version.py +0 -0
  35. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.github/workflows/code_quality.yml +0 -0
  36. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.github/workflows/commit_hygiene.yml +0 -0
  37. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.github/workflows/generate_docs.yml +0 -0
  38. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.github/workflows/semantic_release.yml +0 -0
  39. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.gitignore +0 -0
  40. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.pre-commit-config.yaml +0 -0
  41. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/.releaserc +0 -0
  42. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/LICENSE +0 -0
  43. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/README.md +0 -0
  44. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/commitlint.config.js +0 -0
  45. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/api_reference.rst +0 -0
  46. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/app_structure/index.rst +0 -0
  47. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/app_structure/pre-commit-config.yaml.rst +0 -0
  48. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/app_structure/pyproject.toml.rst +0 -0
  49. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/app_structure/src_app.rst +0 -0
  50. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/changelog.rst +0 -0
  51. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/cli_reference.rst +0 -0
  52. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/conf.py +0 -0
  53. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/custom_views/index.rst +0 -0
  54. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/custom_views/reusable_components.md +0 -0
  55. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/custom_views/templates.md +0 -0
  56. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/custom_views/view_handlers.md +0 -0
  57. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/getting_started/defining_asset.rst +0 -0
  58. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/getting_started/first_action.rst +0 -0
  59. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/getting_started/index.rst +0 -0
  60. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/getting_started/init_app.rst +0 -0
  61. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/getting_started/installation.rst +0 -0
  62. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/getting_started/testing_and_building.rst +0 -0
  63. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/docs/index.rst +0 -0
  64. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/__init__.py +0 -0
  65. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/abstract.py +0 -0
  66. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/action_results.py +0 -0
  67. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/apis/__init__.py +0 -0
  68. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/apis/artifact.py +0 -0
  69. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/apis/container.py +0 -0
  70. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/apis/utils.py +0 -0
  71. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/apis/vault.py +0 -0
  72. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/app_cli_runner.py +0 -0
  73. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/app_client.py +0 -0
  74. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/app_templates/basic_app/.gitignore +0 -0
  75. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/app_templates/basic_app/.pre-commit-config.yaml +0 -0
  76. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/app_templates/basic_app/logo.svg +0 -0
  77. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/app_templates/basic_app/logo_dark.svg +0 -0
  78. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/app_templates/basic_app/src/__init__.py +0 -0
  79. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/app_templates/basic_app/uv.lock +0 -0
  80. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/asset.py +0 -0
  81. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/async_utils.py +0 -0
  82. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/__init__.py +0 -0
  83. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/cli.py +0 -0
  84. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/init/__init__.py +0 -0
  85. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/init/cli.py +0 -0
  86. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/manifests/__init__.py +0 -0
  87. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/manifests/cli.py +0 -0
  88. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/manifests/deserializers.py +0 -0
  89. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/manifests/processors.py +0 -0
  90. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/manifests/serializers.py +0 -0
  91. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/package/cli.py +0 -0
  92. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/package/utils.py +0 -0
  93. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/path_utils.py +0 -0
  94. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/cli/utils.py +0 -0
  95. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/code_renderers/__init__.py +0 -0
  96. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/code_renderers/asset_renderer.py +0 -0
  97. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/code_renderers/renderer.py +0 -0
  98. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/code_renderers/templates/pyproject.toml.jinja +0 -0
  99. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/code_renderers/toml_renderer.py +0 -0
  100. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/colors.py +0 -0
  101. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/compat.py +0 -0
  102. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/crypto.py +0 -0
  103. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/decorators/action.py +0 -0
  104. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/decorators/make_request.py +0 -0
  105. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/decorators/on_poll.py +0 -0
  106. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/decorators/test_connectivity.py +0 -0
  107. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/decorators/view_handler.py +0 -0
  108. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/decorators/webhook.py +0 -0
  109. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/exceptions.py +0 -0
  110. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/field_utils.py +0 -0
  111. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/input_spec.py +0 -0
  112. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/logging.py +0 -0
  113. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/meta/__init__.py +0 -0
  114. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/meta/actions.py +0 -0
  115. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/meta/adapters.py +0 -0
  116. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/meta/app.py +0 -0
  117. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/meta/datatypes.py +0 -0
  118. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/meta/webhooks.py +0 -0
  119. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/models/artifact.py +0 -0
  120. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/models/finding.py +0 -0
  121. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/models/vault_attachment.py +0 -0
  122. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/models/view.py +0 -0
  123. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/paths.py +0 -0
  124. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/py.typed +0 -0
  125. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/action_result.py +0 -0
  126. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/app.py +0 -0
  127. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/base_connector.py +0 -0
  128. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/connector_result.py +0 -0
  129. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/consts.py +0 -0
  130. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/encryption_helper.py +0 -0
  131. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/install_info.py +0 -0
  132. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/json_keys.py +0 -0
  133. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/ph_ipc.py +0 -0
  134. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom/vault.py +0 -0
  135. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom_common/app_interface/app_interface.py +0 -0
  136. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/shims/phantom_common/encryption/encryption_manager_factory.py +0 -0
  137. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/templates/base/base_template.html +0 -0
  138. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/templates/base/error.html +0 -0
  139. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/templates/base/header.html +0 -0
  140. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/templates/base/logo_header.html +0 -0
  141. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/templates/components/pie_chart.html +0 -0
  142. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/templates/widgets/widget_resize_snippet.html +0 -0
  143. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/templates/widgets/widget_template.html +0 -0
  144. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/types.py +0 -0
  145. {splunk_soar_sdk-3.1.0/src/soar_sdk/models → splunk_soar_sdk-3.2.1/src/soar_sdk/views}/__init__.py +0 -0
  146. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/views/component_registry.py +0 -0
  147. {splunk_soar_sdk-3.1.0/src/soar_sdk/views → splunk_soar_sdk-3.2.1/src/soar_sdk/views/components}/__init__.py +0 -0
  148. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/views/components/pie_chart.py +0 -0
  149. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/views/template_filters.py +0 -0
  150. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/views/template_renderer.py +0 -0
  151. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/views/view_parser.py +0 -0
  152. {splunk_soar_sdk-3.1.0/src/soar_sdk/views/components → splunk_soar_sdk-3.2.1/src/soar_sdk/webhooks}/__init__.py +0 -0
  153. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/webhooks/models.py +0 -0
  154. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/src/soar_sdk/webhooks/routing.py +0 -0
  155. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/test.png +0 -0
  156. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/test.txt +0 -0
  157. {splunk_soar_sdk-3.1.0/src/soar_sdk/webhooks → splunk_soar_sdk-3.2.1/tests}/__init__.py +0 -0
  158. {splunk_soar_sdk-3.1.0/tests → splunk_soar_sdk-3.2.1/tests/cli}/__init__.py +0 -0
  159. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/datapath_parse.py +0 -0
  160. {splunk_soar_sdk-3.1.0/tests/cli → splunk_soar_sdk-3.2.1/tests/cli/manifests}/__init__.py +0 -0
  161. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/manifests/test_processors.py +0 -0
  162. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/test_assets/converted_app/actions.py.txt +0 -0
  163. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/test_cli.py +0 -0
  164. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/test_convert_cli.py +0 -0
  165. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/test_deserializers.py +0 -0
  166. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/test_init_cli.py +0 -0
  167. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/test_manifests_cli.py +0 -0
  168. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/test_package_cli.py +0 -0
  169. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/test_serializers.py +0 -0
  170. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/cli/test_utils.py +0 -0
  171. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/code_renderers/test_action_renderer.py +0 -0
  172. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/conftest.py +0 -0
  173. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/example_asset.json +0 -0
  174. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/logo.svg +0 -0
  175. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/logo_dark.svg +0 -0
  176. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/pyproject.toml +0 -0
  177. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/release_notes/v1.md +0 -0
  178. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/src/__init__.py +0 -0
  179. {splunk_soar_sdk-3.1.0/tests/cli/manifests → splunk_soar_sdk-3.2.1/tests/example_app/src/actions}/__init__.py +0 -0
  180. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/src/actions/async_action.py +0 -0
  181. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/src/actions/generate_category.py +0 -0
  182. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/src/actions/reverse_string.py +0 -0
  183. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/src/ignoreme.txt +0 -0
  184. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app/templates/reverse_string.html +0 -0
  185. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app_with_webhook/logo.svg +0 -0
  186. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app_with_webhook/logo_dark.svg +0 -0
  187. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app_with_webhook/pyproject.toml +0 -0
  188. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app_with_webhook/src/__init__.py +0 -0
  189. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/example_app_with_webhook/src/app.py +0 -0
  190. {splunk_soar_sdk-3.1.0/tests/example_app/src/actions → splunk_soar_sdk-3.2.1/tests/interfaces}/__init__.py +0 -0
  191. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/interfaces/test_artifact_interface.py +0 -0
  192. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/interfaces/test_container_interface.py +0 -0
  193. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/interfaces/test_vault_interface.py +0 -0
  194. {splunk_soar_sdk-3.1.0/tests/interfaces → splunk_soar_sdk-3.2.1/tests/meta}/__init__.py +0 -0
  195. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/meta/test_actions.py +0 -0
  196. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/meta/test_adapters.py +0 -0
  197. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/meta/test_datatypes.py +0 -0
  198. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/meta/test_webhooks.py +0 -0
  199. {splunk_soar_sdk-3.1.0/tests/meta → splunk_soar_sdk-3.2.1/tests/mocks}/__init__.py +0 -0
  200. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/mocks/dynamic_mocks.py +0 -0
  201. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/mocks/importable_action.py +0 -0
  202. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/stubs.py +0 -0
  203. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_action_results.py +0 -0
  204. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_actions_manager.py +0 -0
  205. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_app.py +0 -0
  206. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_app_action.py +0 -0
  207. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_app_action_params.py +0 -0
  208. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_app_action_results.py +0 -0
  209. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_app_client.py +0 -0
  210. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_app_runner.py +0 -0
  211. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_asset.py +0 -0
  212. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_assets/splunk-sdk-2.1.0.tar.gz +0 -0
  213. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_async_integration.py +0 -0
  214. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_async_utils.py +0 -0
  215. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_code_renderers.py +0 -0
  216. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_compat.py +0 -0
  217. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_container.py +0 -0
  218. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_custom_views.py +0 -0
  219. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_encryption.py +0 -0
  220. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_field_utils.py +0 -0
  221. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_finding.py +0 -0
  222. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_input_spec.py +0 -0
  223. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_logging.py +0 -0
  224. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_make_request_action.py +0 -0
  225. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_on_poll.py +0 -0
  226. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_params.py +0 -0
  227. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_template_filters.py +0 -0
  228. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_template_renderer.py +0 -0
  229. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_test_connectivity.py +0 -0
  230. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/test_view_parser.py +0 -0
  231. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/tests/webhooks/test_models.py +0 -0
  232. {splunk_soar_sdk-3.1.0 → splunk_soar_sdk-3.2.1}/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.1.0
3
+ Version: 3.2.1
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "splunk-soar-sdk"
3
- version = "3.1.0"
3
+ version = "3.2.1"
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.14"
@@ -0,0 +1,21 @@
1
+ ## [3.2.1](https://github.com/phantomcyber/splunk-soar-sdk/compare/3.2.0...3.2.1) (2025-11-06)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * fix abi3 wheel matching ([719028d](https://github.com/phantomcyber/splunk-soar-sdk/commit/719028dfe3e76f78e4a60b9c3487dba8ca0015d7))
7
+
8
+
9
+
10
+
11
+
12
+ ## [3.2.1](https://github.com/phantomcyber/splunk-soar-sdk/compare/3.2.0...3.2.1) (2025-11-06)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * fix abi3 wheel matching ([719028d](https://github.com/phantomcyber/splunk-soar-sdk/commit/719028dfe3e76f78e4a60b9c3487dba8ca0015d7))
18
+
19
+
20
+
21
+
@@ -0,0 +1 @@
1
+ 3.2.1
@@ -154,6 +154,13 @@ class ActionsManager(BaseConnector):
154
154
  # For non-broker just proceed as we did before
155
155
  return super().get_app_dir()
156
156
 
157
+ def send_finding_to_es(self, finding: dict[str, Any]) -> str:
158
+ """Send finding to ES.
159
+
160
+ Returns finding_id: ID for the finding in ES.
161
+ """
162
+ return ""
163
+
157
164
  @classmethod
158
165
  def get_soar_base_url(cls) -> str:
159
166
  """Get the base URL of the Splunk SOAR instance this app is running on."""
@@ -38,6 +38,7 @@ from soar_sdk.decorators import (
38
38
  ActionDecorator,
39
39
  ViewHandlerDecorator,
40
40
  OnPollDecorator,
41
+ OnESPollDecorator,
41
42
  WebhookDecorator,
42
43
  MakeRequestDecorator,
43
44
  )
@@ -495,6 +496,31 @@ class App:
495
496
  """
496
497
  return OnPollDecorator(self)
497
498
 
499
+ def on_es_poll(self) -> OnESPollDecorator:
500
+ """Decorator for the on_es_poll action.
501
+
502
+ The decorated function must be a generator (using yield) or return an Iterator that yields tuples of (Finding, list[AttachmentInput]). Only one on_es_poll action is allowed per app.
503
+
504
+ Example:
505
+ >>> @app.on_es_poll()
506
+ ... def on_es_poll(
507
+ ... params: OnESPollParams, soar: SOARClient, asset: Asset
508
+ ... ) -> Iterator[tuple[Finding, list[AttachmentInput]]]:
509
+ ... yield (
510
+ ... Finding(
511
+ ... rule_title="Risk threshold exceeded for user",
512
+ ... rule_description="Risk Threshold Exceeded for an object over a 24 hour period",
513
+ ... security_domain="threat",
514
+ ... risk_object="bad_user@splunk.com",
515
+ ... risk_object_type="user",
516
+ ... risk_score=100.0,
517
+ ... status="New",
518
+ ... ),
519
+ ... [],
520
+ ... )
521
+ """
522
+ return OnESPollDecorator(self)
523
+
498
524
  def view_handler(
499
525
  self,
500
526
  *,
@@ -103,6 +103,62 @@ class ActionRenderer(AstRenderer[ActionMeta]):
103
103
  ctx=ast.Load(),
104
104
  ),
105
105
  ),
106
+ "on es poll": ast.FunctionDef(
107
+ name="on_es_poll",
108
+ args=ast.arguments(
109
+ posonlyargs=[],
110
+ args=[
111
+ ast.arg(
112
+ arg="soar", annotation=ast.Name(id="SOARClient", ctx=ast.Load())
113
+ ),
114
+ ast.arg(
115
+ arg="asset", annotation=ast.Name(id="Asset", ctx=ast.Load())
116
+ ),
117
+ ast.arg(
118
+ arg="params",
119
+ annotation=ast.Name(id="OnESPollParams", ctx=ast.Load()),
120
+ ),
121
+ ],
122
+ vararg=None,
123
+ kwonlyargs=[],
124
+ kw_defaults=[],
125
+ kwarg=None,
126
+ defaults=[],
127
+ ),
128
+ body=[
129
+ ast.Raise(
130
+ ast.Call(
131
+ func=ast.Name(id="NotImplementedError"), args=[], keywords=[]
132
+ )
133
+ )
134
+ ],
135
+ decorator_list=[
136
+ ast.Call(
137
+ func=ast.Name(id="app.on_es_poll", ctx=ast.Load()),
138
+ args=[],
139
+ keywords=[],
140
+ )
141
+ ],
142
+ returns=ast.Subscript(
143
+ value=ast.Name(id="Iterator", ctx=ast.Load()),
144
+ slice=ast.Subscript(
145
+ value=ast.Name(id="tuple", ctx=ast.Load()),
146
+ slice=ast.Tuple(
147
+ elts=[
148
+ ast.Name(id="Finding", ctx=ast.Load()),
149
+ ast.Subscript(
150
+ value=ast.Name(id="list", ctx=ast.Load()),
151
+ slice=ast.Name(id="AttachmentInput", ctx=ast.Load()),
152
+ ctx=ast.Load(),
153
+ ),
154
+ ],
155
+ ctx=ast.Load(),
156
+ ),
157
+ ctx=ast.Load(),
158
+ ),
159
+ ctx=ast.Load(),
160
+ ),
161
+ ),
106
162
  }
107
163
 
108
164
  @property
@@ -80,6 +80,7 @@ class AppRenderer:
80
80
  ast.alias(name="Param", asname=None),
81
81
  ast.alias(name="Params", asname=None),
82
82
  ast.alias(name="OnPollParams", asname=None),
83
+ ast.alias(name="OnESPollParams", asname=None),
83
84
  ],
84
85
  level=0,
85
86
  )
@@ -114,6 +115,16 @@ class AppRenderer:
114
115
  names=[ast.alias(name="Artifact", asname=None)],
115
116
  level=0,
116
117
  )
118
+ yield ast.ImportFrom(
119
+ module="soar_sdk.models.finding",
120
+ names=[ast.alias(name="Finding", asname=None)],
121
+ level=0,
122
+ )
123
+ yield ast.ImportFrom(
124
+ module="soar_sdk.models.attachment_input",
125
+ names=[ast.alias(name="AttachmentInput", asname=None)],
126
+ level=0,
127
+ )
117
128
 
118
129
  def create_app_constructor(self) -> ast.Assign:
119
130
  """Create the App class constructor.
@@ -4,6 +4,7 @@ from .action import ActionDecorator
4
4
  from .test_connectivity import ConnectivityTestDecorator
5
5
  from .view_handler import ViewHandlerDecorator
6
6
  from .on_poll import OnPollDecorator
7
+ from .on_es_poll import OnESPollDecorator
7
8
  from .webhook import WebhookDecorator
8
9
  from .make_request import MakeRequestDecorator
9
10
 
@@ -11,6 +12,7 @@ __all__ = [
11
12
  "ActionDecorator",
12
13
  "ConnectivityTestDecorator",
13
14
  "MakeRequestDecorator",
15
+ "OnESPollDecorator",
14
16
  "OnPollDecorator",
15
17
  "ViewHandlerDecorator",
16
18
  "WebhookDecorator",
@@ -0,0 +1,218 @@
1
+ import inspect
2
+ from functools import wraps
3
+ from typing import Any
4
+ from collections.abc import Callable
5
+ from collections.abc import Iterator
6
+
7
+ from soar_sdk.abstract import SOARClient
8
+ from soar_sdk.action_results import ActionResult
9
+ from soar_sdk.params import OnESPollParams
10
+ from soar_sdk.meta.actions import ActionMeta
11
+ from soar_sdk.types import Action, action_protocol
12
+ from soar_sdk.exceptions import ActionFailure
13
+ from soar_sdk.async_utils import run_async_if_needed
14
+ from soar_sdk.logging import getLogger
15
+ from soar_sdk.models.finding import Finding
16
+ from soar_sdk.models.attachment_input import AttachmentInput
17
+ from soar_sdk.models.container import Container
18
+
19
+ from typing import TYPE_CHECKING
20
+
21
+ if TYPE_CHECKING:
22
+ from soar_sdk.app import App
23
+
24
+
25
+ class OnESPollDecorator:
26
+ """Class-based decorator for tagging a function as the special 'on es poll' action."""
27
+
28
+ def __init__(self, app: "App") -> None:
29
+ self.app = app
30
+
31
+ def __call__(self, function: Callable) -> Action:
32
+ """Decorator for the 'on es poll' action.
33
+
34
+ The decorated function must be a generator (using yield) or return an Iterator that yields tuples of (Finding, list[AttachmentInput]). Only one on_es_poll action is allowed per app.
35
+
36
+ Usage:
37
+ Each yielded tuple creates a Container from the Finding metadata. All AttachmentInput items in the list are added as vault attachments to that container.
38
+ """
39
+ if self.app.actions_manager.get_action("on_es_poll"):
40
+ raise TypeError(
41
+ "The 'on_es_poll' decorator can only be used once per App instance."
42
+ )
43
+
44
+ is_generator = inspect.isgeneratorfunction(function)
45
+ is_async_generator = inspect.isasyncgenfunction(function)
46
+ signature = inspect.signature(function)
47
+
48
+ has_iterator_return = (
49
+ signature.return_annotation != inspect.Signature.empty
50
+ and getattr(signature.return_annotation, "__origin__", None) is Iterator
51
+ )
52
+
53
+ if not (is_generator or is_async_generator or has_iterator_return):
54
+ raise TypeError(
55
+ "The on_es_poll function must be a generator (use 'yield') or return an Iterator."
56
+ )
57
+
58
+ action_identifier = "on_es_poll"
59
+ action_name = "on es poll"
60
+
61
+ validated_params_class = OnESPollParams
62
+ logger = getLogger()
63
+
64
+ @action_protocol
65
+ @wraps(function)
66
+ def inner(
67
+ params: OnESPollParams,
68
+ soar: SOARClient = self.app.soar_client,
69
+ *args: Any, # noqa: ANN401
70
+ **kwargs: Any, # noqa: ANN401
71
+ ) -> bool:
72
+ try:
73
+ try:
74
+ action_params = validated_params_class.parse_obj(params)
75
+ except Exception as e:
76
+ logger.info(f"Parameter validation error: {e!s}")
77
+ return self.app._adapt_action_result(
78
+ ActionResult(
79
+ status=False, message=f"Invalid parameters: {e!s}"
80
+ ),
81
+ self.app.actions_manager,
82
+ )
83
+
84
+ kwargs = self.app._build_magic_args(function, soar=soar, **kwargs)
85
+
86
+ result = function(action_params, *args, **kwargs)
87
+ result = run_async_if_needed(result)
88
+
89
+ for item in result:
90
+ if not isinstance(item, tuple) or len(item) != 2:
91
+ logger.info(
92
+ f"Warning: Expected tuple of (Finding, list[AttachmentInput]), got: {type(item)}"
93
+ )
94
+ continue
95
+
96
+ finding, attachments = item
97
+
98
+ if not isinstance(finding, Finding):
99
+ logger.info(
100
+ f"Warning: First element must be Finding, got: {type(finding)}"
101
+ )
102
+ continue
103
+
104
+ if not isinstance(attachments, list):
105
+ logger.info(
106
+ f"Warning: Second element must be list[AttachmentInput], got: {type(attachments)}"
107
+ )
108
+ continue
109
+
110
+ for attachment in attachments:
111
+ if not isinstance(attachment, AttachmentInput):
112
+ logger.info(
113
+ f"Warning: Attachment must be AttachmentInput, got: {type(attachment)}"
114
+ )
115
+ break
116
+ else:
117
+ finding_dict = finding.to_dict()
118
+ logger.info(
119
+ f"Processing finding: {finding_dict.get('rule_title', 'Unnamed finding')}"
120
+ )
121
+
122
+ # Send finding to ES and get finding_id back
123
+ finding_id = self.app.actions_manager.send_finding_to_es(
124
+ finding_dict
125
+ )
126
+
127
+ container = Container(
128
+ name=finding.rule_title,
129
+ description=finding.rule_description,
130
+ severity=finding.urgency or "medium",
131
+ status=finding.status,
132
+ owner_id=finding.owner,
133
+ sensitivity=finding.disposition,
134
+ tags=finding.source,
135
+ external_id=finding_id,
136
+ data={
137
+ "security_domain": finding.security_domain,
138
+ "risk_score": finding.risk_score,
139
+ "risk_object": finding.risk_object,
140
+ "risk_object_type": finding.risk_object_type,
141
+ },
142
+ )
143
+
144
+ ret_val, message, container_id = (
145
+ self.app.actions_manager.save_container(container.to_dict())
146
+ )
147
+ logger.info(
148
+ f"Creating container for finding: {finding.rule_title}"
149
+ )
150
+
151
+ if not ret_val:
152
+ logger.info(f"Failed to create container: {message}")
153
+ continue
154
+
155
+ for attachment in attachments:
156
+ try:
157
+ if attachment.file_content is not None:
158
+ vault_id = soar.vault.create_attachment(
159
+ container_id=container_id,
160
+ file_content=attachment.file_content,
161
+ file_name=attachment.file_name,
162
+ metadata=attachment.metadata,
163
+ )
164
+ else:
165
+ vault_id = soar.vault.add_attachment(
166
+ container_id=container_id,
167
+ file_location=attachment.file_location,
168
+ file_name=attachment.file_name,
169
+ metadata=attachment.metadata,
170
+ )
171
+ logger.info(
172
+ f"Added attachment {attachment.file_name} with vault_id: {vault_id}"
173
+ )
174
+ except Exception as e:
175
+ logger.info(
176
+ f"Failed to add attachment {attachment.file_name}: {e!s}"
177
+ )
178
+
179
+ return self.app._adapt_action_result(
180
+ ActionResult(status=True, message="Finding processing complete"),
181
+ self.app.actions_manager,
182
+ )
183
+ except ActionFailure as e:
184
+ e.set_action_name(action_name)
185
+ return self.app._adapt_action_result(
186
+ ActionResult(status=False, message=str(e)),
187
+ self.app.actions_manager,
188
+ )
189
+ except Exception as e:
190
+ self.app.actions_manager.add_exception(e)
191
+ logger.info(f"Error during finding processing: {e!s}")
192
+ return self.app._adapt_action_result(
193
+ ActionResult(status=False, message=str(e)),
194
+ self.app.actions_manager,
195
+ )
196
+
197
+ inner.params_class = validated_params_class
198
+
199
+ class OnESPollActionMeta(ActionMeta):
200
+ def model_dump(self, *args: object, **kwargs: object) -> dict[str, Any]:
201
+ data = super().model_dump(*args, **kwargs)
202
+ data["output"] = []
203
+ return data
204
+
205
+ inner.meta = OnESPollActionMeta(
206
+ action=action_name,
207
+ identifier=action_identifier,
208
+ description=inspect.getdoc(function) or action_name,
209
+ verbose="Callback action for the on_es_poll ingest functionality",
210
+ type="ingest",
211
+ read_only=True,
212
+ parameters=validated_params_class,
213
+ versions="EQ(*)",
214
+ )
215
+
216
+ self.app.actions_manager.set_action(action_identifier, inner)
217
+ self.app._dev_skip_in_pytest(function, inner)
218
+ return inner
@@ -277,9 +277,7 @@ class UvPackage(BaseModel):
277
277
  for abi in abi_precedence:
278
278
  abi_wheels = [wheel for wheel in self.wheels if abi in wheel.abi_tags]
279
279
  for python in python_precedence:
280
- python_wheels = [
281
- wheel for wheel in abi_wheels if python in wheel.python_tags
282
- ]
280
+ python_wheels = self._filter_python_wheels(abi_wheels, python, abi)
283
281
  for platform in platform_precedence:
284
282
  platform_wheels = [
285
283
  wheel
@@ -293,6 +291,54 @@ class UvPackage(BaseModel):
293
291
  f"Could not find a suitable wheel for {self.name=}, {self.version=}, {abi_precedence=}, {python_precedence=}, {platform_precedence=}"
294
292
  )
295
293
 
294
+ def _filter_python_wheels(
295
+ self, wheels: list[UvWheel], target_python: str, abi: str
296
+ ) -> list[UvWheel]:
297
+ """Filter and sort wheels by Python version compatibility.
298
+
299
+ For abi3 wheels, prefers the highest compatible minimum version
300
+ (e.g., cp311-abi3 over cp38-abi3 for Python 3.13).
301
+ """
302
+ compatible = [
303
+ wheel
304
+ for wheel in wheels
305
+ if self._is_python_compatible(wheel, target_python, abi)
306
+ ]
307
+
308
+ # For abi3 wheels, prefer highest minimum version (closest to target)
309
+ if abi == "abi3" and compatible:
310
+ compatible = sorted(
311
+ compatible,
312
+ key=lambda w: max(
313
+ (int(tag[2:]) for tag in w.python_tags if tag.startswith("cp")),
314
+ default=0,
315
+ ),
316
+ reverse=True,
317
+ )
318
+
319
+ return compatible
320
+
321
+ def _is_python_compatible(
322
+ self, wheel: UvWheel, target_python: str, abi: str
323
+ ) -> bool:
324
+ """Check if a wheel is compatible with the target Python version.
325
+
326
+ For abi3 wheels, the Python tag indicates minimum version (e.g., cp311-abi3 works with Python ≥3.11).
327
+ For non-abi3 wheels, exact tag matching is required.
328
+ """
329
+ if target_python in wheel.python_tags:
330
+ return True
331
+
332
+ # For abi3 wheels, check if target >= minimum version (e.g., cp313 >= cp311)
333
+ if abi == "abi3":
334
+ return any(
335
+ int(tag[2:]) <= int(target_python[2:])
336
+ for tag in wheel.python_tags
337
+ if tag.startswith("cp") and target_python.startswith("cp")
338
+ )
339
+
340
+ return False
341
+
296
342
  _manylinux_precedence: ClassVar[list[str]] = [
297
343
  "_2_28", # glibc 2.28, latest stable version, supports Ubuntu 18.10+ and RHEL/Oracle 8+
298
344
  "_2_17", # glibc 2.17, LTS-ish, supports Ubuntu 13.10+ and RHEL/Oracle 7+
@@ -0,0 +1,15 @@
1
+ from .artifact import Artifact
2
+ from .attachment_input import AttachmentInput
3
+ from .container import Container
4
+ from .finding import Finding, DrilldownSearch, DrilldownDashboard
5
+ from .vault_attachment import VaultAttachment
6
+
7
+ __all__ = [
8
+ "Artifact",
9
+ "AttachmentInput",
10
+ "Container",
11
+ "DrilldownDashboard",
12
+ "DrilldownSearch",
13
+ "Finding",
14
+ "VaultAttachment",
15
+ ]
@@ -0,0 +1,27 @@
1
+ from pydantic import BaseModel, field_validator, ConfigDict
2
+ from pydantic_core.core_schema import ValidationInfo
3
+
4
+
5
+ class AttachmentInput(BaseModel):
6
+ """Represents a vault attachment to be created during on_es_poll.
7
+
8
+ Specify either file_content OR file_location, not both.
9
+ """
10
+
11
+ model_config = ConfigDict(extra="forbid")
12
+
13
+ file_content: str | bytes | None = None
14
+ file_location: str | None = None
15
+ file_name: str
16
+ metadata: dict[str, str] | None = None
17
+
18
+ @field_validator("file_location")
19
+ @classmethod
20
+ def validate_one_source(cls, v: str | None, info: ValidationInfo) -> str | None:
21
+ """Validate that exactly one of file_content or file_location is provided."""
22
+ file_content = info.data.get("file_content")
23
+ if v is None and file_content is None:
24
+ raise ValueError("Must provide either file_content or file_location")
25
+ if v is not None and file_content is not None:
26
+ raise ValueError("Cannot provide both file_content and file_location")
27
+ return v
@@ -14,6 +14,7 @@ class Container(BaseModel):
14
14
  label: str | None = None
15
15
  description: str | None = None
16
16
  source_data_identifier: str | None = None
17
+ external_id: str | None = None
17
18
  severity: str | None = None
18
19
  status: str | None = None
19
20
  tags: list[str] | str | None = None
@@ -193,6 +193,25 @@ class OnPollParams(Params):
193
193
  )
194
194
 
195
195
 
196
+ class OnESPollParams(Params):
197
+ """Canonical parameters for the special 'on es poll' action."""
198
+
199
+ start_time: int = Param(
200
+ description="Start of time range, in epoch time (milliseconds).",
201
+ required=False,
202
+ )
203
+
204
+ end_time: int = Param(
205
+ description="End of time range, in epoch time (milliseconds).",
206
+ required=False,
207
+ )
208
+
209
+ container_count: int = Param(
210
+ description="Maximum number of container records to query for.",
211
+ required=False,
212
+ )
213
+
214
+
196
215
  class MakeRequestParams(Params):
197
216
  """Canonical parameters for the special make request action."""
198
217