Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
-->
# Unreleased version
## Backward incompatibility
* The deprecated Snowpark annotation processor (`processors: [snowpark]` in a Native App project definition) is now opt-in. Bundling or deploying a Native App with a `snowpark` processor no longer executes it unless the `ENABLE_SNOWPARK_ANNOTATION_PROCESSOR` feature flag is turned on (`snowflake.cli.features.enable_snowpark_annotation_processor = true` in `config.toml`, or `SNOWFLAKE_CLI_FEATURES_ENABLE_SNOWPARK_ANNOTATION_PROCESSOR=1`). The processor's job is to execute every annotated `.py` artifact in a subprocess so that module-level objects can be introspected; gating it behind an explicit opt-in aligns its long-deprecated status with its cost and closes an arbitrary-code-execution path for repository-level attack scenarios.

## Deprecations

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from snowflake.cli.api.cli_global_context import get_cli_context, span
from snowflake.cli.api.console import cli_console
from snowflake.cli.api.console import cli_console as cc
from snowflake.cli.api.feature_flags import FeatureFlag
from snowflake.cli.api.metrics import CLICounterField
from snowflake.cli.api.project.schemas.entities.common import (
PathMapping,
Expand Down Expand Up @@ -170,6 +171,10 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

@staticmethod
def is_enabled() -> bool:
return FeatureFlag.ENABLE_SNOWPARK_ANNOTATION_PROCESSOR.is_enabled()

@span("snowpark_processor")
def process(
self,
Expand Down
3 changes: 3 additions & 0 deletions src/snowflake/cli/api/feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class FeatureFlag(FeatureFlagMixin):
ENABLE_NATIVE_APP_PYTHON_SETUP = BooleanFlag(
"ENABLE_NATIVE_APP_PYTHON_SETUP", False
)
ENABLE_SNOWPARK_ANNOTATION_PROCESSOR = BooleanFlag(
"ENABLE_SNOWPARK_ANNOTATION_PROCESSOR", False
)
ENABLE_NATIVE_APP_CHILDREN = BooleanFlag("ENABLE_NATIVE_APP_CHILDREN", False)
# TODO 4.0: remove ENABLE_RELEASE_CHANNELS
ENABLE_RELEASE_CHANNELS = BooleanFlag("ENABLE_RELEASE_CHANNELS", None)
Expand Down
16 changes: 16 additions & 0 deletions tests/nativeapp/test_feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,19 @@ def test_feature_setup_script_generation_enabled(
mock_get_config_value.assert_called_once_with(
"cli", "features", key="enable_native_app_python_setup", default=None
)


@mock.patch("snowflake.cli.api.config.get_config_value")
@pytest.mark.parametrize("value_from_config", [True, False])
def test_feature_snowpark_annotation_processor_enabled(
mock_get_config_value, value_from_config
):
mock_get_config_value.return_value = value_from_config

assert (
FeatureFlag.ENABLE_SNOWPARK_ANNOTATION_PROCESSOR.is_enabled()
is value_from_config
)
mock_get_config_value.assert_called_once_with(
"cli", "features", key="enable_snowpark_annotation_processor", default=None
)
Loading