splunk-soar-sdk 3.8.1__tar.gz → 3.9.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.
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/PKG-INFO +1 -1
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/api_reference.rst +10 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/pyproject.toml +1 -1
- splunk_soar_sdk-3.9.0/release_notes.txt +33 -0
- splunk_soar_sdk-3.9.0/release_version.txt +1 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/action_results.py +36 -36
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/asset.py +75 -10
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/manifests/serializers.py +12 -3
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/code_renderers/action_renderer.py +56 -22
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/code_renderers/asset_renderer.py +8 -4
- splunk_soar_sdk-3.9.0/src/soar_sdk/field_utils.py +92 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/models/finding.py +11 -5
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/params.py +48 -8
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_convert_cli.py +6 -6
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/code_renderers/test_action_renderer.py +19 -4
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/conftest.py +7 -2
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_action_results.py +3 -3
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_app.py +27 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_asset.py +26 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_code_renderers.py +2 -2
- splunk_soar_sdk-3.9.0/tests/test_field_utils.py +33 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_params.py +15 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/uv.lock +1 -1
- splunk_soar_sdk-3.8.1/release_notes.txt +0 -21
- splunk_soar_sdk-3.8.1/release_version.txt +0 -1
- splunk_soar_sdk-3.8.1/src/soar_sdk/field_utils.py +0 -8
- splunk_soar_sdk-3.8.1/tests/test_field_utils.py +0 -19
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/ISSUE_TEMPLATE/bug.md +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/pull_request_template.md +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/scripts/generate_test_summary.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/utils/github.js +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/utils/update_version.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/workflows/code_quality.yml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/workflows/commit_hygiene.yml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/workflows/generate_docs.yml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/workflows/integration_tests.yml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.github/workflows/semantic_release.yml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.gitignore +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.pre-commit-config.yaml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/.releaserc +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/LICENSE +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/README.md +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/commitlint.config.js +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/app_structure/index.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/app_structure/pre-commit-config.yaml.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/app_structure/pyproject.toml.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/app_structure/src_app.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/authentication/basic.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/authentication/index.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/authentication/oauth.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/authentication/static_token.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/changelog.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/cli_reference.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/conf.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/custom_views/index.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/custom_views/reusable_components.md +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/custom_views/templates.md +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/custom_views/view_handlers.md +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/getting_started/defining_asset.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/getting_started/first_action.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/getting_started/index.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/getting_started/init_app.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/getting_started/installation.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/getting_started/testing_and_building.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/docs/index.rst +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/README.md +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/install.sh +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/mcp_config.json +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/pyproject.toml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/pytest.ini +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/src/soar_test_assistant/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/src/soar_test_assistant/server.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/tests/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/tests/test_analyzer.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/mcp_server/uv.lock +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/abstract.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/actions_manager.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/apis/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/apis/artifact.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/apis/container.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/apis/es/findings.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/apis/utils.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/apis/vault.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/app.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/app_cli_runner.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/app_client.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/app_templates/basic_app/.gitignore +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/app_templates/basic_app/.pre-commit-config.yaml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/app_templates/basic_app/logo.svg +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/app_templates/basic_app/logo_dark.svg +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/app_templates/basic_app/src/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/app_templates/basic_app/uv.lock +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/asset_state.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/async_utils.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/auth/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/auth/client.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/auth/factories.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/auth/flows.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/auth/httpx_auth.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/auth/models.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/init/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/init/cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/manifests/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/manifests/cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/manifests/deserializers.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/manifests/processors.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/package/cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/package/utils.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/path_utils.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/test/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/test/cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/cli/utils.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/code_renderers/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/code_renderers/app_renderer.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/code_renderers/renderer.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/code_renderers/templates/pyproject.toml.jinja +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/code_renderers/toml_renderer.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/colors.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/compat.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/crypto.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/decorators/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/decorators/action.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/decorators/make_request.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/decorators/on_es_poll.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/decorators/on_poll.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/decorators/test_connectivity.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/decorators/view_handler.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/decorators/webhook.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/es_client.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/exceptions.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/extras/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/extras/email/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/extras/email/processor.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/extras/email/rfc5322.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/extras/email/utils.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/input_spec.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/logging.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/meta/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/meta/actions.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/meta/adapters.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/meta/app.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/meta/datatypes.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/meta/dependencies.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/meta/webhooks.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/models/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/models/artifact.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/models/attachment_input.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/models/container.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/models/vault_attachment.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/models/view.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/paths.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/py.typed +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/action_result.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/app.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/base_connector.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/connector_result.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/consts.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/encryption_helper.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/install_info.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/json_keys.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/ph_ipc.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom/vault.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom_common/app_interface/app_interface.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/shims/phantom_common/encryption/encryption_manager_factory.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/templates/base/base_template.html +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/templates/base/error.html +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/templates/base/header.html +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/templates/base/logo_header.html +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/templates/components/pie_chart.html +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/templates/widgets/widget_resize_snippet.html +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/templates/widgets/widget_template.html +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/types.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/views/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/views/component_registry.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/views/components/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/views/components/pie_chart.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/views/template_filters.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/views/template_renderer.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/views/view_parser.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/webhooks/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/webhooks/models.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/webhooks/routing.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/datapath_parse.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/manifests/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/manifests/test_processors.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/manifests/test_python_version_resolution.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_assets/converted_app/actions.py.txt +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_deserializers.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_init_cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_manifests_cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_package_cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_serializers.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_test_cli.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/cli/test_utils.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/app.json +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/example_asset.json +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/logo.svg +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/logo_dark.svg +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/pyproject.toml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/release_notes/v1.md +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/src/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/src/actions/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/src/actions/async_action.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/src/actions/generate_category.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/src/actions/reverse_string.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/src/app.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/src/ignoreme.txt +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/templates/reverse_string.html +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app/uv.lock +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_oauth/example_asset.json +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_oauth/logo.svg +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_oauth/logo_dark.svg +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_oauth/pyproject.toml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_oauth/src/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_oauth/src/app.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_webhook/app.json +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_webhook/example_asset.json +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_webhook/logo.svg +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_webhook/logo_dark.svg +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_webhook/pyproject.toml +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_webhook/src/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_webhook/src/app.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/example_app_with_webhook/uv.lock +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/integration/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/integration/conftest.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/integration/phantom_constants.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/integration/phantom_instance.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/integration/soar_client.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/integration/test_example_app.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/integration/test_example_app_with_webhook.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/interfaces/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/interfaces/test_artifact_interface.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/interfaces/test_container_interface.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/interfaces/test_vault_interface.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/meta/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/meta/test_actions.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/meta/test_adapters.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/meta/test_datatypes.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/meta/test_dependencies.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/meta/test_webhooks.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/mocks/__init__.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/mocks/dynamic_mocks.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/mocks/importable_action.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/stubs.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_actions_manager.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_app_action.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_app_action_params.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_app_action_results.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_app_client.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_app_runner.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_asset_state.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_assets/splunk-sdk-2.1.0.tar.gz +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_async_integration.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_async_utils.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_attachment_input.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_compat.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_container.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_custom_views.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_email_processor.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_encryption.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_es_client.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_es_on_poll.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_finding.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_input_spec.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_logging.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_make_request_action.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_oauth_certificate_client.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_oauth_client.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_oauth_factories.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_oauth_flows.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_oauth_httpx_auth.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_oauth_models.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_on_poll.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_rfc5322.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_template_filters.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_template_renderer.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_test_connectivity.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/test_view_parser.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/tests/webhooks/test_models.py +0 -0
- {splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.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.
|
|
3
|
+
Version: 3.9.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
|
|
@@ -51,6 +51,11 @@ AssetField
|
|
|
51
51
|
|
|
52
52
|
.. autofunction:: soar_sdk.asset.AssetField
|
|
53
53
|
|
|
54
|
+
.. note::
|
|
55
|
+
The ``required`` keyword argument is deprecated and will be removed in the next
|
|
56
|
+
major version. Prefer Optional type hints (``str | None``) to indicate optional
|
|
57
|
+
asset fields.
|
|
58
|
+
|
|
54
59
|
BaseAsset
|
|
55
60
|
^^^^^^^^^
|
|
56
61
|
|
|
@@ -101,6 +106,11 @@ For example, let's give the ``uid`` field a Common Event Format (CEF) type and m
|
|
|
101
106
|
is_admin: bool
|
|
102
107
|
uid: int = Param(required=False, cef_types=["user id"])
|
|
103
108
|
|
|
109
|
+
.. note::
|
|
110
|
+
The ``required`` keyword argument is deprecated and will be removed in the next
|
|
111
|
+
major version. Prefer Optional type hints (``str | None``) to indicate optional
|
|
112
|
+
action parameters.
|
|
113
|
+
|
|
104
114
|
Defining Parameters
|
|
105
115
|
^^^^^^^^^^^^^^^^^^^
|
|
106
116
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# [3.9.0](https://github.com/phantomcyber/splunk-soar-sdk/compare/3.8.2...3.9.0) (2026-02-03)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* allow optional type annotations in params/fields ([8b4ab75](https://github.com/phantomcyber/splunk-soar-sdk/commit/8b4ab75419241949baa93c67fea3c218c20956c8))
|
|
7
|
+
* don't render required=True because it's redundant ([7fe4842](https://github.com/phantomcyber/splunk-soar-sdk/commit/7fe4842d1822e007e9ccf44e07a5575ab023bba7))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* use new union syntax for soarapps convert ([121020e](https://github.com/phantomcyber/splunk-soar-sdk/commit/121020ec6465d2ce7fce4dda862d961feafe024f))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# [3.9.0](https://github.com/phantomcyber/splunk-soar-sdk/compare/3.8.2...3.9.0) (2026-02-03)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* allow optional type annotations in params/fields ([8b4ab75](https://github.com/phantomcyber/splunk-soar-sdk/commit/8b4ab75419241949baa93c67fea3c218c20956c8))
|
|
24
|
+
* don't render required=True because it's redundant ([7fe4842](https://github.com/phantomcyber/splunk-soar-sdk/commit/7fe4842d1822e007e9ccf44e07a5575ab023bba7))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* use new union syntax for soarapps convert ([121020e](https://github.com/phantomcyber/splunk-soar-sdk/commit/121020ec6465d2ce7fce4dda862d961feafe024f))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.9.0
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import itertools
|
|
2
|
-
import types
|
|
3
2
|
from collections.abc import Iterator
|
|
4
|
-
from typing import Any, NotRequired
|
|
3
|
+
from typing import Any, NotRequired
|
|
5
4
|
|
|
6
|
-
from pydantic import BaseModel, ConfigDict, Field
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
7
6
|
from typing_extensions import TypedDict
|
|
8
7
|
|
|
9
8
|
from soar_sdk.compat import remove_when_soar_newer_than
|
|
10
|
-
from soar_sdk.field_utils import parse_json_schema_extra
|
|
9
|
+
from soar_sdk.field_utils import normalize_field_annotation, parse_json_schema_extra
|
|
11
10
|
from soar_sdk.meta.datatypes import as_datatype
|
|
12
11
|
from soar_sdk.shims.phantom.action_result import ActionResult as PhantomActionResult
|
|
13
12
|
|
|
@@ -164,13 +163,32 @@ class ActionOutput(BaseModel):
|
|
|
164
163
|
... ) # Model fields can't start with an underscore, so we're using an alias to create the proper JSON key
|
|
165
164
|
|
|
166
165
|
Note:
|
|
167
|
-
Fields cannot be Union
|
|
166
|
+
Fields cannot be Union types other than Optional.
|
|
168
167
|
Nested ActionOutput classes are supported for complex data structures.
|
|
169
168
|
"""
|
|
170
169
|
|
|
171
170
|
# Allow instantiation with both field names and aliases for backward compatibility
|
|
172
171
|
model_config = ConfigDict(populate_by_name=True)
|
|
173
172
|
|
|
173
|
+
@model_validator(mode="before")
|
|
174
|
+
@classmethod
|
|
175
|
+
def _apply_optional_defaults(cls, values: Any) -> Any: # noqa: ANN401
|
|
176
|
+
"""Populate missing optional fields with ``None`` before validation."""
|
|
177
|
+
for field_name, field in cls.model_fields.items():
|
|
178
|
+
if field_name in values or (field.alias and field.alias in values):
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
normalized = normalize_field_annotation(
|
|
182
|
+
field.annotation,
|
|
183
|
+
field_name=field_name,
|
|
184
|
+
context="Output",
|
|
185
|
+
allow_list=True,
|
|
186
|
+
)
|
|
187
|
+
if normalized.is_optional:
|
|
188
|
+
values[field_name] = None
|
|
189
|
+
|
|
190
|
+
return values
|
|
191
|
+
|
|
174
192
|
@classmethod
|
|
175
193
|
def _to_json_schema(
|
|
176
194
|
cls,
|
|
@@ -193,7 +211,7 @@ class ActionOutput(BaseModel):
|
|
|
193
211
|
OutputFieldSpecification objects describing each field in the schema.
|
|
194
212
|
|
|
195
213
|
Raises:
|
|
196
|
-
TypeError: If a field type cannot be serialized, is Union
|
|
214
|
+
TypeError: If a field type cannot be serialized, is an unsupported Union,
|
|
197
215
|
or if a nested ActionOutput type is encountered incorrectly.
|
|
198
216
|
|
|
199
217
|
Note:
|
|
@@ -211,36 +229,18 @@ class ActionOutput(BaseModel):
|
|
|
211
229
|
if field_type is None:
|
|
212
230
|
continue
|
|
213
231
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if len(type_args) != 1:
|
|
227
|
-
raise TypeError(
|
|
228
|
-
f"Output field {field_name} is invalid: List types must have exactly one non-null type argument."
|
|
229
|
-
)
|
|
230
|
-
datapath += ".*"
|
|
231
|
-
else:
|
|
232
|
-
if len(type_args) != 1:
|
|
233
|
-
raise TypeError(
|
|
234
|
-
f"Output field {field_name} is invalid: the only valid Union type is Optional, or Union[X, None]."
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
field_type = type_args[0]
|
|
238
|
-
origin = get_origin(field_type)
|
|
239
|
-
|
|
240
|
-
if not isinstance(field_type, type):
|
|
241
|
-
raise TypeError(
|
|
242
|
-
f"Output field {field_name} has invalid type annotation: {field_type}"
|
|
243
|
-
)
|
|
232
|
+
normalized = normalize_field_annotation(
|
|
233
|
+
field_type,
|
|
234
|
+
field_name=field_name,
|
|
235
|
+
context="Output",
|
|
236
|
+
allow_list=True,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
datapath = (
|
|
240
|
+
parent_datapath + f".{field_name}" + (".*" * normalized.list_depth)
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
field_type = normalized.base_type
|
|
244
244
|
|
|
245
245
|
if issubclass(field_type, ActionOutput):
|
|
246
246
|
# If the field is another ActionOutput, recursively call _to_json_schema
|
|
@@ -9,7 +9,11 @@ from typing_extensions import TypedDict
|
|
|
9
9
|
from soar_sdk.asset_state import AssetState
|
|
10
10
|
from soar_sdk.compat import remove_when_soar_newer_than
|
|
11
11
|
from soar_sdk.exceptions import AppContextRequired
|
|
12
|
-
from soar_sdk.field_utils import
|
|
12
|
+
from soar_sdk.field_utils import (
|
|
13
|
+
normalize_field_annotation,
|
|
14
|
+
parse_json_schema_extra,
|
|
15
|
+
resolve_required,
|
|
16
|
+
)
|
|
13
17
|
from soar_sdk.input_spec import AppConfig
|
|
14
18
|
from soar_sdk.meta.datatypes import as_datatype
|
|
15
19
|
|
|
@@ -28,7 +32,7 @@ class FieldCategory(str, Enum):
|
|
|
28
32
|
|
|
29
33
|
def AssetField(
|
|
30
34
|
description: str | None = None,
|
|
31
|
-
required: bool =
|
|
35
|
+
required: bool | None = None,
|
|
32
36
|
default: Any | None = None, # noqa: ANN401
|
|
33
37
|
value_list: list | None = None,
|
|
34
38
|
sensitive: bool = False,
|
|
@@ -41,7 +45,9 @@ def AssetField(
|
|
|
41
45
|
Args:
|
|
42
46
|
description: Human-friendly label for the field shown in the asset form.
|
|
43
47
|
required: Whether the field must be provided. When True and ``default`` is
|
|
44
|
-
``None``, the field is marked as required in the manifest.
|
|
48
|
+
``None``, the field is marked as required in the manifest. Deprecated:
|
|
49
|
+
this will be removed in the next major version. Prefer Optional type
|
|
50
|
+
hints (``str | None``) to indicate optional fields.
|
|
45
51
|
default: Default value for optional fields. Ignored when ``required`` is
|
|
46
52
|
True and no explicit default is provided.
|
|
47
53
|
value_list: Optional dropdown options presented to the user.
|
|
@@ -65,13 +71,20 @@ def AssetField(
|
|
|
65
71
|
json_schema_extra["is_file"] = True
|
|
66
72
|
|
|
67
73
|
# Use ... for required fields
|
|
68
|
-
field_default: Any =
|
|
74
|
+
field_default: Any = (
|
|
75
|
+
... if default is None and (required is True or required is None) else default
|
|
76
|
+
)
|
|
77
|
+
validate_default = None
|
|
78
|
+
if required is False and default is None:
|
|
79
|
+
# Preserve legacy optional behavior for non-optional type hints
|
|
80
|
+
validate_default = False
|
|
69
81
|
|
|
70
82
|
return Field(
|
|
71
83
|
default=field_default,
|
|
72
84
|
description=description,
|
|
73
85
|
alias=alias,
|
|
74
86
|
json_schema_extra=json_schema_extra,
|
|
87
|
+
validate_default=validate_default,
|
|
75
88
|
)
|
|
76
89
|
|
|
77
90
|
|
|
@@ -137,6 +150,25 @@ class BaseAsset(BaseModel):
|
|
|
137
150
|
arbitrary_types_allowed=True,
|
|
138
151
|
)
|
|
139
152
|
|
|
153
|
+
@model_validator(mode="before")
|
|
154
|
+
@classmethod
|
|
155
|
+
def _apply_optional_defaults(cls, values: Any) -> Any: # noqa: ANN401
|
|
156
|
+
"""Populate missing optional fields with ``None`` before validation."""
|
|
157
|
+
for field_name, field in cls.model_fields.items():
|
|
158
|
+
if field_name in values or (field.alias and field.alias in values):
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
normalized = normalize_field_annotation(
|
|
162
|
+
field.annotation,
|
|
163
|
+
field_name=field_name,
|
|
164
|
+
context="Asset field",
|
|
165
|
+
allow_list=False,
|
|
166
|
+
)
|
|
167
|
+
if normalized.is_optional:
|
|
168
|
+
values[field_name] = None
|
|
169
|
+
|
|
170
|
+
return values
|
|
171
|
+
|
|
140
172
|
@model_validator(mode="before")
|
|
141
173
|
@classmethod
|
|
142
174
|
def validate_no_reserved_fields(cls, values: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -211,8 +243,15 @@ class BaseAsset(BaseModel):
|
|
|
211
243
|
if field_type is None:
|
|
212
244
|
continue
|
|
213
245
|
|
|
246
|
+
normalized = normalize_field_annotation(
|
|
247
|
+
field_type,
|
|
248
|
+
field_name=field_name,
|
|
249
|
+
context="Asset field",
|
|
250
|
+
allow_list=False,
|
|
251
|
+
)
|
|
252
|
+
|
|
214
253
|
try:
|
|
215
|
-
type_name = as_datatype(
|
|
254
|
+
type_name = as_datatype(normalized.base_type)
|
|
216
255
|
except TypeError as e:
|
|
217
256
|
raise TypeError(
|
|
218
257
|
f"Failed to serialize asset field {field_name}: {e}"
|
|
@@ -221,16 +260,16 @@ class BaseAsset(BaseModel):
|
|
|
221
260
|
json_schema_extra = parse_json_schema_extra(field.json_schema_extra)
|
|
222
261
|
|
|
223
262
|
if json_schema_extra.get("sensitive", False):
|
|
224
|
-
if
|
|
263
|
+
if normalized.base_type is not str:
|
|
225
264
|
raise TypeError(
|
|
226
|
-
f"Sensitive parameter {field_name} must be type str, not {
|
|
265
|
+
f"Sensitive parameter {field_name} must be type str, not {normalized.base_type.__name__}"
|
|
227
266
|
)
|
|
228
267
|
type_name = "password"
|
|
229
268
|
|
|
230
269
|
if json_schema_extra.get("is_file", False):
|
|
231
|
-
if
|
|
270
|
+
if normalized.base_type is not str:
|
|
232
271
|
raise TypeError(
|
|
233
|
-
f"File parameter {field_name} must be type str, not {
|
|
272
|
+
f"File parameter {field_name} must be type str, not {normalized.base_type.__name__}"
|
|
234
273
|
)
|
|
235
274
|
type_name = "file"
|
|
236
275
|
|
|
@@ -239,7 +278,7 @@ class BaseAsset(BaseModel):
|
|
|
239
278
|
|
|
240
279
|
params_field = AssetFieldSpecification(
|
|
241
280
|
data_type=type_name,
|
|
242
|
-
required=
|
|
281
|
+
required=resolve_required(json_schema_extra, normalized.is_optional),
|
|
243
282
|
description=description,
|
|
244
283
|
order=field_order,
|
|
245
284
|
category=json_schema_extra.get("category", FieldCategory.CONNECTIVITY),
|
|
@@ -300,3 +339,29 @@ class BaseAsset(BaseModel):
|
|
|
300
339
|
if self._ingest_state is None:
|
|
301
340
|
raise AppContextRequired()
|
|
302
341
|
return self._ingest_state
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class ESIngestMixin:
|
|
345
|
+
"""Mixin for apps that support ES polling (on_es_poll).
|
|
346
|
+
|
|
347
|
+
Add this mixin to your Asset class to include ES Ingest Settings fields.
|
|
348
|
+
These fields are configured in the ES UI and control how findings are created.
|
|
349
|
+
|
|
350
|
+
Example:
|
|
351
|
+
>>> class Asset(BaseAsset, ESIngestMixin):
|
|
352
|
+
... server: str = AssetField(description="API server URL")
|
|
353
|
+
... api_key: str = AssetField(description="API key", sensitive=True)
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
es_security_domain: str = AssetField(
|
|
357
|
+
required=False,
|
|
358
|
+
description="Security domain for ES findings",
|
|
359
|
+
default="threat",
|
|
360
|
+
category=FieldCategory.INGEST,
|
|
361
|
+
)
|
|
362
|
+
es_urgency: str = AssetField(
|
|
363
|
+
required=False,
|
|
364
|
+
description="Urgency level for ES findings",
|
|
365
|
+
default="medium",
|
|
366
|
+
category=FieldCategory.INGEST,
|
|
367
|
+
)
|
|
@@ -4,7 +4,7 @@ from logging import getLogger
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
from soar_sdk.action_results import ActionOutput, OutputFieldSpecification
|
|
7
|
-
from soar_sdk.field_utils import parse_json_schema_extra
|
|
7
|
+
from soar_sdk.field_utils import normalize_field_annotation, parse_json_schema_extra
|
|
8
8
|
from soar_sdk.meta.datatypes import as_datatype
|
|
9
9
|
from soar_sdk.params import Params
|
|
10
10
|
|
|
@@ -42,9 +42,18 @@ class OutputsSerializer:
|
|
|
42
42
|
if annotation is None:
|
|
43
43
|
continue
|
|
44
44
|
|
|
45
|
+
normalized = normalize_field_annotation(
|
|
46
|
+
annotation,
|
|
47
|
+
field_name=field_name,
|
|
48
|
+
context="Action parameter",
|
|
49
|
+
allow_list=False,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
parameter_name = field.alias or field_name
|
|
53
|
+
|
|
45
54
|
spec = OutputFieldSpecification(
|
|
46
|
-
data_path=f"action_result.parameter.{
|
|
47
|
-
data_type=as_datatype(
|
|
55
|
+
data_path=f"action_result.parameter.{parameter_name}",
|
|
56
|
+
data_type=as_datatype(normalized.base_type),
|
|
48
57
|
)
|
|
49
58
|
|
|
50
59
|
json_schema_extra = parse_json_schema_extra(field.json_schema_extra)
|
{splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/code_renderers/action_renderer.py
RENAMED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import ast
|
|
2
|
-
import typing
|
|
3
2
|
from collections.abc import Iterator
|
|
4
3
|
from typing import ClassVar
|
|
5
4
|
|
|
@@ -8,7 +7,11 @@ from pydantic_core import PydanticUndefined
|
|
|
8
7
|
from soar_sdk.action_results import ActionOutput
|
|
9
8
|
from soar_sdk.cli.utils import normalize_field_name
|
|
10
9
|
from soar_sdk.code_renderers.renderer import AstRenderer
|
|
11
|
-
from soar_sdk.field_utils import
|
|
10
|
+
from soar_sdk.field_utils import (
|
|
11
|
+
normalize_field_annotation,
|
|
12
|
+
parse_json_schema_extra,
|
|
13
|
+
resolve_required,
|
|
14
|
+
)
|
|
12
15
|
from soar_sdk.meta.actions import ActionMeta
|
|
13
16
|
from soar_sdk.params import Params
|
|
14
17
|
|
|
@@ -171,6 +174,23 @@ class ActionRenderer(AstRenderer[ActionMeta]):
|
|
|
171
174
|
"""
|
|
172
175
|
return self.context
|
|
173
176
|
|
|
177
|
+
@staticmethod
|
|
178
|
+
def _build_annotation(base_type: type, list_depth: int, required: bool) -> ast.expr:
|
|
179
|
+
annotation: ast.expr = ast.Name(id=base_type.__name__, ctx=ast.Load())
|
|
180
|
+
for _ in range(list_depth):
|
|
181
|
+
annotation = ast.Subscript(
|
|
182
|
+
value=ast.Name(id="list", ctx=ast.Load()),
|
|
183
|
+
slice=annotation,
|
|
184
|
+
ctx=ast.Load(),
|
|
185
|
+
)
|
|
186
|
+
if not required:
|
|
187
|
+
annotation = ast.BinOp(
|
|
188
|
+
left=annotation,
|
|
189
|
+
op=ast.BitOr(),
|
|
190
|
+
right=ast.Constant(value=None),
|
|
191
|
+
)
|
|
192
|
+
return annotation
|
|
193
|
+
|
|
174
194
|
def render_ast(self) -> Iterator[ast.stmt]:
|
|
175
195
|
"""Generates the AST for the action.
|
|
176
196
|
|
|
@@ -284,16 +304,15 @@ class ActionRenderer(AstRenderer[ActionMeta]):
|
|
|
284
304
|
if annotation is None:
|
|
285
305
|
continue
|
|
286
306
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if not isinstance(annotation, type):
|
|
294
|
-
continue
|
|
307
|
+
normalized = normalize_field_annotation(
|
|
308
|
+
annotation,
|
|
309
|
+
field_name=field_name_str,
|
|
310
|
+
context="Output",
|
|
311
|
+
allow_list=True,
|
|
312
|
+
)
|
|
295
313
|
|
|
296
|
-
|
|
314
|
+
json_schema_extra = parse_json_schema_extra(field.json_schema_extra)
|
|
315
|
+
required = resolve_required(json_schema_extra, normalized.is_optional)
|
|
297
316
|
|
|
298
317
|
field_name = normalize_field_name(field_name_str)
|
|
299
318
|
if field.alias is not None and field.alias != field_name.normalized:
|
|
@@ -302,14 +321,16 @@ class ActionRenderer(AstRenderer[ActionMeta]):
|
|
|
302
321
|
|
|
303
322
|
field_def_ast = ast.AnnAssign(
|
|
304
323
|
target=ast.Name(id=field_name.normalized, ctx=ast.Store()),
|
|
305
|
-
annotation=
|
|
324
|
+
annotation=self._build_annotation(
|
|
325
|
+
normalized.base_type, normalized.list_depth, required
|
|
326
|
+
),
|
|
306
327
|
simple=1,
|
|
307
328
|
)
|
|
308
329
|
|
|
309
|
-
if
|
|
330
|
+
if issubclass(normalized.base_type, ActionOutput):
|
|
310
331
|
# If the field is a Pydantic model, recursively print its fields
|
|
311
332
|
# In Pydantic v2, use annotation directly (no field.type_)
|
|
312
|
-
for model_ast in self.render_outputs_ast(
|
|
333
|
+
for model_ast in self.render_outputs_ast(normalized.base_type):
|
|
313
334
|
model_tree[model_ast.name] = model_ast
|
|
314
335
|
|
|
315
336
|
if field_name.modified:
|
|
@@ -325,7 +346,7 @@ class ActionRenderer(AstRenderer[ActionMeta]):
|
|
|
325
346
|
)
|
|
326
347
|
else:
|
|
327
348
|
keywords = []
|
|
328
|
-
extras = {**
|
|
349
|
+
extras = {**json_schema_extra}
|
|
329
350
|
|
|
330
351
|
if extras or field_name.modified:
|
|
331
352
|
extras["example_values"] = extras.pop("examples", None)
|
|
@@ -388,7 +409,18 @@ class ActionRenderer(AstRenderer[ActionMeta]):
|
|
|
388
409
|
if field_def.annotation is None:
|
|
389
410
|
continue
|
|
390
411
|
|
|
391
|
-
|
|
412
|
+
normalized = normalize_field_annotation(
|
|
413
|
+
field_def.annotation,
|
|
414
|
+
field_name=field_name,
|
|
415
|
+
context="Action parameter",
|
|
416
|
+
allow_list=False,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
json_schema_extra = parse_json_schema_extra(field_def.json_schema_extra)
|
|
420
|
+
required = resolve_required(json_schema_extra, normalized.is_optional)
|
|
421
|
+
field_type = self._build_annotation(
|
|
422
|
+
normalized.base_type, normalized.list_depth, required
|
|
423
|
+
)
|
|
392
424
|
|
|
393
425
|
param = ast.Call(
|
|
394
426
|
func=ast.Name(id="Param", ctx=ast.Load()),
|
|
@@ -396,8 +428,6 @@ class ActionRenderer(AstRenderer[ActionMeta]):
|
|
|
396
428
|
keywords=[],
|
|
397
429
|
)
|
|
398
430
|
|
|
399
|
-
json_schema_extra = parse_json_schema_extra(field_def.json_schema_extra)
|
|
400
|
-
|
|
401
431
|
if field_def.description:
|
|
402
432
|
param.keywords.append(
|
|
403
433
|
ast.keyword(
|
|
@@ -405,10 +435,6 @@ class ActionRenderer(AstRenderer[ActionMeta]):
|
|
|
405
435
|
value=ast.Constant(value=field_def.description),
|
|
406
436
|
)
|
|
407
437
|
)
|
|
408
|
-
if not json_schema_extra.get("required", True):
|
|
409
|
-
param.keywords.append(
|
|
410
|
-
ast.keyword(arg="required", value=ast.Constant(value=False))
|
|
411
|
-
)
|
|
412
438
|
if json_schema_extra.get("primary", False):
|
|
413
439
|
param.keywords.append(
|
|
414
440
|
ast.keyword(arg="primary", value=ast.Constant(value=True))
|
|
@@ -445,6 +471,14 @@ class ActionRenderer(AstRenderer[ActionMeta]):
|
|
|
445
471
|
ast.keyword(arg="allow_list", value=ast.Constant(value=True))
|
|
446
472
|
)
|
|
447
473
|
|
|
474
|
+
if field_def.alias and field_def.alias != field_name:
|
|
475
|
+
param.keywords.append(
|
|
476
|
+
ast.keyword(
|
|
477
|
+
arg="alias",
|
|
478
|
+
value=ast.Constant(value=field_def.alias),
|
|
479
|
+
)
|
|
480
|
+
)
|
|
481
|
+
|
|
448
482
|
field_def_ast = ast.AnnAssign(
|
|
449
483
|
target=ast.Name(id=field_name, ctx=ast.Store()),
|
|
450
484
|
annotation=field_type,
|
{splunk_soar_sdk-3.8.1 → splunk_soar_sdk-3.9.0}/src/soar_sdk/code_renderers/asset_renderer.py
RENAMED
|
@@ -56,11 +56,15 @@ class AssetRenderer(AstRenderer[list[AssetContext]]):
|
|
|
56
56
|
|
|
57
57
|
for field in self.context:
|
|
58
58
|
field_name = ast.Name(id=field.name, ctx=ast.Store())
|
|
59
|
-
field_type = ast.Name(id=field.py_type, ctx=ast.Load())
|
|
59
|
+
field_type: ast.expr = ast.Name(id=field.py_type, ctx=ast.Load())
|
|
60
|
+
if not field.required:
|
|
61
|
+
field_type = ast.BinOp(
|
|
62
|
+
left=field_type,
|
|
63
|
+
op=ast.BitOr(),
|
|
64
|
+
right=ast.Constant(value=None),
|
|
65
|
+
)
|
|
60
66
|
|
|
61
|
-
field_kwargs = [
|
|
62
|
-
ast.keyword(arg="required", value=ast.Constant(value=field.required)),
|
|
63
|
-
]
|
|
67
|
+
field_kwargs = []
|
|
64
68
|
if field.description is not None:
|
|
65
69
|
field_kwargs.append(
|
|
66
70
|
ast.keyword(
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import types
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any, Union, get_args, get_origin
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def parse_json_schema_extra(json_schema_extra: Any) -> dict[str, Any]: # noqa: ANN401
|
|
7
|
+
"""Extract json_schema_extra as a dict, handling both dict and callable forms."""
|
|
8
|
+
if callable(json_schema_extra):
|
|
9
|
+
return {}
|
|
10
|
+
return json_schema_extra or {}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class NormalizedFieldType:
|
|
15
|
+
"""Normalized field annotation details."""
|
|
16
|
+
|
|
17
|
+
base_type: type
|
|
18
|
+
list_depth: int
|
|
19
|
+
is_optional: bool
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def normalize_field_annotation(
|
|
23
|
+
annotation: Any, # noqa: ANN401
|
|
24
|
+
*,
|
|
25
|
+
field_name: str,
|
|
26
|
+
context: str,
|
|
27
|
+
allow_list: bool,
|
|
28
|
+
) -> NormalizedFieldType:
|
|
29
|
+
"""Normalize an annotation by unwrapping Optional and list types.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
annotation: The field annotation to normalize.
|
|
33
|
+
field_name: Name of the field for error reporting.
|
|
34
|
+
context: Context string for error reporting (e.g., "parameter").
|
|
35
|
+
allow_list: Whether list types are allowed.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
NormalizedFieldType describing the base type, list nesting, and optionality.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
TypeError: If unsupported unions or list shapes are encountered.
|
|
42
|
+
"""
|
|
43
|
+
list_depth = 0
|
|
44
|
+
is_optional = False
|
|
45
|
+
|
|
46
|
+
while True:
|
|
47
|
+
origin = get_origin(annotation)
|
|
48
|
+
if origin is list:
|
|
49
|
+
type_args = get_args(annotation)
|
|
50
|
+
if len(type_args) != 1:
|
|
51
|
+
raise TypeError(
|
|
52
|
+
f"{context} field {field_name} is invalid: list types must have exactly one type argument."
|
|
53
|
+
)
|
|
54
|
+
list_depth += 1
|
|
55
|
+
annotation = type_args[0]
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
if origin in (types.UnionType, Union):
|
|
59
|
+
# types.UnionType is for `X | Y` (Python 3.10+)
|
|
60
|
+
type_args = tuple(
|
|
61
|
+
arg for arg in get_args(annotation) if arg is not type(None)
|
|
62
|
+
)
|
|
63
|
+
if len(type_args) != 1:
|
|
64
|
+
raise TypeError(
|
|
65
|
+
f"{context} field {field_name} is invalid: only Optional[T] is supported for unions."
|
|
66
|
+
)
|
|
67
|
+
is_optional = True
|
|
68
|
+
annotation = type_args[0]
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
if list_depth and not allow_list:
|
|
74
|
+
raise TypeError(
|
|
75
|
+
f"{context} field {field_name} is invalid: list types are not supported."
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if not isinstance(annotation, type):
|
|
79
|
+
raise TypeError(
|
|
80
|
+
f"{context} field {field_name} has invalid type annotation: {annotation}"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return NormalizedFieldType(
|
|
84
|
+
base_type=annotation, list_depth=list_depth, is_optional=is_optional
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def resolve_required(json_schema_extra: dict[str, Any], is_optional: bool) -> bool:
|
|
89
|
+
"""Resolve required flag using JSON schema metadata and optionality."""
|
|
90
|
+
if "required" in json_schema_extra:
|
|
91
|
+
return bool(json_schema_extra.get("required"))
|
|
92
|
+
return not is_optional
|
|
@@ -21,20 +21,26 @@ class DrilldownDashboard(BaseModel):
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class Finding(BaseModel):
|
|
24
|
-
"""Represents a finding to be created during
|
|
24
|
+
"""Represents a finding to be created during on_es_poll.
|
|
25
25
|
|
|
26
26
|
Findings are stored in ES and can be associated with SOAR containers/artifacts
|
|
27
27
|
for investigation workflow.
|
|
28
|
+
|
|
29
|
+
Only rule_title and security_domain are required. All other fields are optional
|
|
30
|
+
and will use ES defaults if not provided.
|
|
28
31
|
"""
|
|
29
32
|
|
|
30
33
|
model_config = ConfigDict(extra="forbid")
|
|
31
34
|
|
|
35
|
+
# Required fields
|
|
32
36
|
rule_title: str
|
|
33
|
-
rule_description: str
|
|
34
37
|
security_domain: str
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
|
|
39
|
+
# Optional fields
|
|
40
|
+
rule_description: str | None = None
|
|
41
|
+
risk_object: str | None = None
|
|
42
|
+
risk_object_type: str | None = None
|
|
43
|
+
risk_score: float | None = None
|
|
38
44
|
status: str | None = None
|
|
39
45
|
urgency: str | None = None
|
|
40
46
|
owner: str | None = None
|