-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
ref(search): Add EAP API attribute visibility checks #116091
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
459f308
85f1e75
3d585d7
1c0058e
c002e60
4859bb3
208c427
409b9a9
62217a5
91d8b48
9b8b7b2
a01633f
661211e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,7 +60,7 @@ | |
| from sentry.search.eap.rpc_utils import and_trace_item_filters | ||
| from sentry.search.eap.sampling import validate_sampling | ||
| from sentry.search.eap.spans.attributes import SPANS_INTERNAL_TO_PUBLIC_ALIAS_MAPPINGS | ||
| from sentry.search.eap.types import EAPResponse, SearchResolverConfig | ||
| from sentry.search.eap.types import EAPResponse, SearchResolverConfig, SupportedTraceItemType | ||
| from sentry.search.events import constants as qb_constants | ||
| from sentry.search.events import fields | ||
| from sentry.search.events import filter as event_filter | ||
|
|
@@ -83,6 +83,10 @@ def collect_issue_short_ids_from_parsed_terms(terms: Sequence[object]) -> set[st | |
| return out | ||
|
|
||
|
|
||
| class _HiddenApiAttribute(InvalidSearchQuery): | ||
| pass | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class SearchResolver: | ||
| """The only attributes are things we want to cache and params | ||
|
|
@@ -531,12 +535,14 @@ def _resolve_term( | |
| self, term: event_search.SearchFilter | ||
| ) -> tuple[TraceItemFilter, VirtualColumnDefinition | None]: | ||
| resolved_column, context_definition = self.resolve_column(term.key.name) | ||
| self._raise_if_hidden_resolved_attribute(term.key.name, resolved_column) | ||
|
|
||
| value = term.value.value | ||
| if self.params.is_timeseries_request and context_definition is not None: | ||
| resolved_column, value = self.map_search_term_context_to_original_column( | ||
| term, context_definition | ||
| ) | ||
| self._raise_if_hidden_api_attribute(term.key.name, resolved_column) | ||
| context_definition = None | ||
|
|
||
| if not isinstance(resolved_column.proto_definition, AttributeKey): | ||
|
|
@@ -812,6 +818,7 @@ def resolve_aggregate_term( | |
| self, term: event_search.AggregateFilter | ||
| ) -> tuple[AggregationFilter, VirtualColumnDefinition | None]: | ||
| resolved_column, context = self.resolve_column(term.key.name) | ||
| self._raise_if_hidden_resolved_attribute(term.key.name, resolved_column) | ||
| proto_definition = resolved_column.proto_definition | ||
|
|
||
| if not isinstance( | ||
|
|
@@ -967,7 +974,14 @@ def resolve_columns( | |
| for column in stripped_columns: | ||
| match = fields.is_function(column) | ||
| has_aggregates = has_aggregates or match is not None | ||
| resolved_column, context = self.resolve_column(column, match) | ||
| try: | ||
| resolved_column, context = self.resolve_column(column, match) | ||
| except _HiddenApiAttribute: | ||
| continue | ||
| if isinstance(resolved_column, ResolvedAttribute) and self._should_hide_api_attribute( | ||
| column, resolved_column | ||
| ): | ||
| continue | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| if ( | ||
| self.config.disable_array_attributes | ||
| and isinstance(resolved_column, ResolvedAttribute) | ||
|
|
@@ -1024,12 +1038,52 @@ def resolve_attributes( | |
| resolved_contexts = [] | ||
| for column in columns: | ||
| col, context = self.resolve_attribute(column) | ||
| self._raise_if_hidden_api_attribute(column, col) | ||
| if self.config.disable_array_attributes and col.internal_type == constants.ARRAY: | ||
| continue | ||
| resolved_columns.append(col) | ||
| resolved_contexts.append(context) | ||
| return resolved_columns, resolved_contexts | ||
|
|
||
| def should_hide_api_column( | ||
| self, column: str, resolved_column: ResolvedAttribute | ResolvedFunction | ||
| ) -> bool: | ||
| if not isinstance(resolved_column, ResolvedAttribute): | ||
| return False | ||
| return self._should_hide_api_attribute(column, resolved_column) | ||
|
|
||
| def _should_hide_api_attribute( | ||
| self, column: str, resolved_attribute: ResolvedAttribute | ||
| ) -> bool: | ||
| if self.config.api_attribute_visibility_item_type is None: | ||
| return False | ||
|
|
||
| from sentry.search.eap.utils import can_expose_attribute_to_api | ||
|
|
||
| item_type = SupportedTraceItemType(self.config.api_attribute_visibility_item_type) | ||
| visibility_attribute = ( | ||
| column | ||
| if column in self.definitions.contexts or column in self.definitions.columns | ||
| else resolved_attribute.internal_name | ||
| ) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Virtual context skips storage visibilityMedium Severity API attribute visibility checks in Reviewed by Cursor Bugbot for commit 661211e. Configure here. |
||
| return not can_expose_attribute_to_api( | ||
| visibility_attribute, | ||
| item_type, | ||
| include_internal=self.config.api_attribute_visibility_include_internal, | ||
| ) | ||
|
|
||
| def _raise_if_hidden_api_attribute( | ||
| self, column: str, resolved_attribute: ResolvedAttribute | ||
| ) -> None: | ||
| if self._should_hide_api_attribute(column, resolved_attribute): | ||
| raise _HiddenApiAttribute(f"The field {column} is not allowed for this query") | ||
|
|
||
| def _raise_if_hidden_resolved_attribute( | ||
| self, column: str, resolved_column: ResolvedAttribute | ResolvedFunction | ||
| ) -> None: | ||
| if isinstance(resolved_column, ResolvedAttribute): | ||
| self._raise_if_hidden_api_attribute(column, resolved_column) | ||
|
|
||
| def resolve_attribute( | ||
| self, column: str, public_alias_override: str | None = None | ||
| ) -> tuple[ResolvedAttribute, VirtualColumnDefinition | None]: | ||
|
|
@@ -1126,7 +1180,10 @@ def resolve_functions( | |
| """Helper function to resolve a list of functions instead of 1 attribute at a time""" | ||
| resolved_functions, resolved_contexts = [], [] | ||
| for column in columns: | ||
| function, context = self.resolve_function(column) | ||
| try: | ||
| function, context = self.resolve_function(column) | ||
| except _HiddenApiAttribute: | ||
| continue | ||
| resolved_functions.append(function) | ||
| resolved_contexts.append(context) | ||
| return resolved_functions, resolved_contexts | ||
|
|
@@ -1182,6 +1239,9 @@ def resolve_function( | |
| parsed_args.append(argument_definition.default_arg) | ||
| else: | ||
| parsed_argument, _ = self.resolve_attribute(argument_definition.default_arg) | ||
| self._raise_if_hidden_api_attribute( | ||
| argument_definition.default_arg, parsed_argument | ||
| ) | ||
| parsed_args.append(parsed_argument) | ||
| missing_args -= 1 | ||
| continue | ||
|
|
@@ -1196,6 +1256,7 @@ def resolve_function( | |
| ) | ||
| if isinstance(argument_definition, AttributeArgumentDefinition): | ||
| parsed_argument, _ = self.resolve_attribute(argument) | ||
| self._raise_if_hidden_api_attribute(argument, parsed_argument) | ||
| parsed_args.append(parsed_argument) | ||
| else: | ||
| if argument_definition.argument_types is None: | ||
|
|
@@ -1282,7 +1343,10 @@ def resolve_equations( | |
| formulas = [] | ||
| contexts = [] | ||
| for equation in equations: | ||
| formula, context = self.resolve_equation(equation) | ||
| try: | ||
| formula, context = self.resolve_equation(equation) | ||
| except _HiddenApiAttribute: | ||
| continue | ||
| formulas.append(formula) | ||
| contexts.extend(context) | ||
| return formulas, contexts | ||
|
|
@@ -1303,6 +1367,8 @@ def resolve_equation( | |
| col, context = self.resolve_column( | ||
| operation, public_alias_override=f"equation|{equation}" | ||
| ) | ||
| if isinstance(col, ResolvedAttribute): | ||
| self._raise_if_hidden_api_attribute(operation, col) | ||
| return col, [context] if context else [] | ||
| elif isinstance(operation, float): | ||
| return ( | ||
|
|
@@ -1378,6 +1444,8 @@ def _resolve_operation( | |
| # Resolve the column, and turn it into a RPC Column so it can be used in a BinaryFormula | ||
| # Columns in equations must pass default_value=0 otherwise they may become a null and ruin the entire formula | ||
| col, context = self.resolve_column(operation, default_value=0) | ||
| if isinstance(col, ResolvedAttribute): | ||
| self._raise_if_hidden_api_attribute(operation, col) | ||
| contexts = [context] if context is not None else [] | ||
| proto_definition = col.proto_definition | ||
|
|
||
|
|
||


Uh oh!
There was an error while loading. Please reload this page.