diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index 8cb9e73f52..a2378abe62 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to the **Prowler API** are documented in this file. ### Added - Support AlibabaCloud provider [(#9485)](https://github.com/prowler-cloud/prowler/pull/9485) +- `provider_id` and `provider_id__in` filter aliases for findings endpoints to enable consistent frontend parameter naming [(#9701)](https://github.com/prowler-cloud/prowler/pull/9701) --- diff --git a/api/src/backend/api/filters.py b/api/src/backend/api/filters.py index f626732477..ddb82c12f4 100644 --- a/api/src/backend/api/filters.py +++ b/api/src/backend/api/filters.py @@ -94,8 +94,12 @@ class ChoiceInFilter(BaseInFilter, ChoiceFilter): class CommonFindingFilters(FilterSet): # We filter providers from the scan in findings + # Both 'provider' and 'provider_id' parameters are supported for API consistency + # Frontend uses 'provider_id' uniformly across all endpoints provider = UUIDFilter(field_name="scan__provider__id", lookup_expr="exact") provider__in = UUIDInFilter(field_name="scan__provider__id", lookup_expr="in") + provider_id = UUIDFilter(field_name="scan__provider__id", lookup_expr="exact") + provider_id__in = UUIDInFilter(field_name="scan__provider__id", lookup_expr="in") provider_type = ChoiceFilter( choices=Provider.ProviderChoices.choices, field_name="scan__provider__provider" ) diff --git a/api/src/backend/api/specs/v1.yaml b/api/src/backend/api/specs/v1.yaml index 2c2bd8f027..9d43537db4 100644 --- a/api/src/backend/api/specs/v1.yaml +++ b/api/src/backend/api/specs/v1.yaml @@ -879,12 +879,28 @@ paths: description: Multiple values may be separated by commas. explode: false style: form + - in: query + name: filter[provider_id] + schema: + type: string + format: uuid + - in: query + name: filter[provider_id__in] + schema: + type: array + items: + type: string + format: uuid + description: Multiple values may be separated by commas. + explode: false + style: form - in: query name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -894,7 +910,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -912,8 +927,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -923,7 +939,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -1436,12 +1451,28 @@ paths: description: Multiple values may be separated by commas. explode: false style: form + - in: query + name: filter[provider_id] + schema: + type: string + format: uuid + - in: query + name: filter[provider_id__in] + schema: + type: array + items: + type: string + format: uuid + description: Multiple values may be separated by commas. + explode: false + style: form - in: query name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -1451,7 +1482,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -1469,8 +1499,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -1480,7 +1511,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -1901,12 +1931,28 @@ paths: description: Multiple values may be separated by commas. explode: false style: form + - in: query + name: filter[provider_id] + schema: + type: string + format: uuid + - in: query + name: filter[provider_id__in] + schema: + type: array + items: + type: string + format: uuid + description: Multiple values may be separated by commas. + explode: false + style: form - in: query name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -1916,7 +1962,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -1934,8 +1979,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -1945,7 +1991,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -2364,12 +2409,28 @@ paths: description: Multiple values may be separated by commas. explode: false style: form + - in: query + name: filter[provider_id] + schema: + type: string + format: uuid + - in: query + name: filter[provider_id__in] + schema: + type: array + items: + type: string + format: uuid + description: Multiple values may be separated by commas. + explode: false + style: form - in: query name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -2379,7 +2440,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -2397,8 +2457,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -2408,7 +2469,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -2815,12 +2875,28 @@ paths: description: Multiple values may be separated by commas. explode: false style: form + - in: query + name: filter[provider_id] + schema: + type: string + format: uuid + - in: query + name: filter[provider_id__in] + schema: + type: array + items: + type: string + format: uuid + description: Multiple values may be separated by commas. + explode: false + style: form - in: query name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -2830,7 +2906,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -2848,8 +2923,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -2859,7 +2935,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -4627,8 +4702,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -4648,14 +4724,16 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -4677,6 +4755,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - name: filter[search] @@ -4782,8 +4861,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -4803,14 +4883,16 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -4832,6 +4914,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - name: filter[search] @@ -4956,8 +5039,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -4967,7 +5051,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -4985,8 +5068,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -4996,7 +5080,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -5149,8 +5232,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -5160,7 +5244,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -5178,8 +5261,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -5189,7 +5273,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -5338,6 +5421,7 @@ paths: schema: type: string enum: + - alibabacloud - aws - azure - gcp @@ -5347,7 +5431,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -5366,6 +5449,7 @@ paths: items: type: string enum: + - alibabacloud - aws - azure - gcp @@ -5375,7 +5459,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -5564,8 +5647,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -5575,7 +5659,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -5593,8 +5676,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -5604,7 +5688,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -5740,8 +5823,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -5751,7 +5835,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -5769,8 +5852,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -5780,7 +5864,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -6563,8 +6646,9 @@ paths: name: filter[provider] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -6574,7 +6658,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -6592,8 +6675,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -6603,7 +6687,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -6623,8 +6706,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -6634,7 +6718,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -6652,8 +6735,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -6663,7 +6747,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -7277,8 +7360,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -7288,7 +7372,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -7306,8 +7389,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -7317,7 +7401,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -7664,8 +7747,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -7675,7 +7759,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -7693,8 +7776,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -7704,7 +7788,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -7946,8 +8029,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -7957,7 +8041,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -7975,8 +8058,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -7986,7 +8070,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -8234,8 +8317,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -8245,7 +8329,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -8263,8 +8346,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -8274,7 +8358,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -9085,8 +9168,9 @@ paths: name: filter[provider_type] schema: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -9096,7 +9180,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -9114,8 +9197,9 @@ paths: type: array items: type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f enum: + - alibabacloud - aws - azure - gcp @@ -9125,7 +9209,6 @@ paths: - m365 - mongodbatlas - oraclecloud - - alibabacloud description: |- Multiple values may be separated by commas. @@ -15637,6 +15720,44 @@ components: required: - atlas_public_key - atlas_private_key + - type: object + title: Alibaba Cloud Static Credentials + properties: + access_key_id: + type: string + description: The Alibaba Cloud access key ID for authentication. + access_key_secret: + type: string + description: The Alibaba Cloud access key secret for authentication. + security_token: + type: string + description: The STS security token for temporary credentials + (optional). + required: + - access_key_id + - access_key_secret + - type: object + title: Alibaba Cloud RAM Role Assumption + properties: + role_arn: + type: string + description: The ARN of the RAM role to assume (e.g., acs:ram::1234567890123456:role/ProwlerRole). + access_key_id: + type: string + description: The Alibaba Cloud access key ID of the RAM user + that will assume the role. + access_key_secret: + type: string + description: The Alibaba Cloud access key secret of the RAM + user that will assume the role. + role_session_name: + type: string + description: An identifier for the role session (optional, + defaults to 'ProwlerSession'). + required: + - role_arn + - access_key_id + - access_key_secret writeOnly: true required: - secret @@ -16647,7 +16768,7 @@ components: * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure * `alibabacloud` - Alibaba Cloud - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f uid: type: string title: Unique identifier for the provider, set by the provider @@ -16764,7 +16885,7 @@ components: - oraclecloud - alibabacloud type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f description: |- Type of provider to create. @@ -16826,7 +16947,7 @@ components: - oraclecloud - alibabacloud type: string - x-spec-enum-id: eca8c51e6bd28935 + x-spec-enum-id: 684bf4173d2b754f description: |- Type of provider to create. @@ -17602,6 +17723,44 @@ components: required: - atlas_public_key - atlas_private_key + - type: object + title: Alibaba Cloud Static Credentials + properties: + access_key_id: + type: string + description: The Alibaba Cloud access key ID for authentication. + access_key_secret: + type: string + description: The Alibaba Cloud access key secret for authentication. + security_token: + type: string + description: The STS security token for temporary credentials + (optional). + required: + - access_key_id + - access_key_secret + - type: object + title: Alibaba Cloud RAM Role Assumption + properties: + role_arn: + type: string + description: The ARN of the RAM role to assume (e.g., acs:ram::1234567890123456:role/ProwlerRole). + access_key_id: + type: string + description: The Alibaba Cloud access key ID of the RAM user that + will assume the role. + access_key_secret: + type: string + description: The Alibaba Cloud access key secret of the RAM user + that will assume the role. + role_session_name: + type: string + description: An identifier for the role session (optional, defaults + to 'ProwlerSession'). + required: + - role_arn + - access_key_id + - access_key_secret writeOnly: true required: - secret_type @@ -17927,6 +18086,44 @@ components: required: - atlas_public_key - atlas_private_key + - type: object + title: Alibaba Cloud Static Credentials + properties: + access_key_id: + type: string + description: The Alibaba Cloud access key ID for authentication. + access_key_secret: + type: string + description: The Alibaba Cloud access key secret for authentication. + security_token: + type: string + description: The STS security token for temporary credentials + (optional). + required: + - access_key_id + - access_key_secret + - type: object + title: Alibaba Cloud RAM Role Assumption + properties: + role_arn: + type: string + description: The ARN of the RAM role to assume (e.g., acs:ram::1234567890123456:role/ProwlerRole). + access_key_id: + type: string + description: The Alibaba Cloud access key ID of the RAM user + that will assume the role. + access_key_secret: + type: string + description: The Alibaba Cloud access key secret of the RAM + user that will assume the role. + role_session_name: + type: string + description: An identifier for the role session (optional, + defaults to 'ProwlerSession'). + required: + - role_arn + - access_key_id + - access_key_secret writeOnly: true required: - secret_type @@ -18266,6 +18463,44 @@ components: required: - atlas_public_key - atlas_private_key + - type: object + title: Alibaba Cloud Static Credentials + properties: + access_key_id: + type: string + description: The Alibaba Cloud access key ID for authentication. + access_key_secret: + type: string + description: The Alibaba Cloud access key secret for authentication. + security_token: + type: string + description: The STS security token for temporary credentials + (optional). + required: + - access_key_id + - access_key_secret + - type: object + title: Alibaba Cloud RAM Role Assumption + properties: + role_arn: + type: string + description: The ARN of the RAM role to assume (e.g., acs:ram::1234567890123456:role/ProwlerRole). + access_key_id: + type: string + description: The Alibaba Cloud access key ID of the RAM user that + will assume the role. + access_key_secret: + type: string + description: The Alibaba Cloud access key secret of the RAM user + that will assume the role. + role_session_name: + type: string + description: An identifier for the role session (optional, defaults + to 'ProwlerSession'). + required: + - role_arn + - access_key_id + - access_key_secret writeOnly: true required: - secret diff --git a/api/src/backend/api/tests/test_views.py b/api/src/backend/api/tests/test_views.py index 6babb92fc8..dc0ec1ac26 100644 --- a/api/src/backend/api/tests/test_views.py +++ b/api/src/backend/api/tests/test_views.py @@ -4110,6 +4110,37 @@ def test_finding_filter_by_provider_id_in( assert response.status_code == status.HTTP_200_OK assert len(response.json()["data"]) == 2 + def test_finding_filter_by_provider_id_alias( + self, authenticated_client, findings_fixture + ): + """Test that provider_id filter alias works identically to provider filter.""" + response = authenticated_client.get( + reverse("finding-list"), + { + "filter[provider_id]": findings_fixture[0].scan.provider.id, + "filter[inserted_at]": TODAY, + }, + ) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()["data"]) == 2 + + def test_finding_filter_by_provider_id_in_alias( + self, authenticated_client, findings_fixture + ): + """Test that provider_id__in filter alias works identically to provider__in filter.""" + response = authenticated_client.get( + reverse("finding-list"), + { + "filter[provider_id__in]": [ + findings_fixture[0].scan.provider.id, + findings_fixture[1].scan.provider.id, + ], + "filter[inserted_at]": TODAY, + }, + ) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()["data"]) == 2 + @pytest.mark.parametrize( "filter_name", ( @@ -4331,6 +4362,28 @@ def test_findings_latest(self, authenticated_client, latest_scan_finding): == latest_scan_finding.status ) + def test_findings_latest_filter_by_provider_id_alias( + self, authenticated_client, latest_scan_finding + ): + """Test that provider_id filter alias works on latest findings endpoint.""" + response = authenticated_client.get( + reverse("finding-latest"), + {"filter[provider_id]": latest_scan_finding.scan.provider.id}, + ) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()["data"]) == 1 + + def test_findings_latest_filter_by_provider_id_in_alias( + self, authenticated_client, latest_scan_finding + ): + """Test that provider_id__in filter alias works on latest findings endpoint.""" + response = authenticated_client.get( + reverse("finding-latest"), + {"filter[provider_id__in]": str(latest_scan_finding.scan.provider.id)}, + ) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()["data"]) == 1 + def test_findings_metadata_latest(self, authenticated_client, latest_scan_finding): response = authenticated_client.get( reverse("finding-metadata_latest"), diff --git a/ui/app/(prowler)/_overview/_components/accounts-selector.tsx b/ui/app/(prowler)/_overview/_components/accounts-selector.tsx index efa33dca5e..dce5f049dc 100644 --- a/ui/app/(prowler)/_overview/_components/accounts-selector.tsx +++ b/ui/app/(prowler)/_overview/_components/accounts-selector.tsx @@ -1,6 +1,6 @@ "use client"; -import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import { ReactNode } from "react"; import { @@ -43,21 +43,10 @@ interface AccountsSelectorProps { export function AccountsSelector({ providers }: AccountsSelectorProps) { const router = useRouter(); - const pathname = usePathname(); const searchParams = useSearchParams(); - // Determine which filter key to use based on the current page - // - /findings uses provider__in (API expects this) - // - /overview and others use provider_id__in - const isFindingsPage = pathname?.startsWith("/findings"); - const filterKey = isFindingsPage - ? "filter[provider__in]" - : "filter[provider_id__in]"; - - // Read from both keys to support navigation between pages - const providerIn = searchParams.get("filter[provider__in]") || ""; - const providerIdIn = searchParams.get("filter[provider_id__in]") || ""; - const current = providerIn || providerIdIn; + const filterKey = "filter[provider_id__in]"; + const current = searchParams.get(filterKey) || ""; const selectedTypes = searchParams.get("filter[provider_type__in]") || ""; const selectedTypesList = selectedTypes ? selectedTypes.split(",").filter(Boolean) @@ -73,13 +62,9 @@ export function AccountsSelector({ providers }: AccountsSelectorProps) { const handleMultiValueChange = (ids: string[]) => { const params = new URLSearchParams(searchParams.toString()); - - // Clean up both potential provider filter keys first - params.delete("filter[provider_id__in]"); - params.delete("filter[provider__in]"); + params.delete(filterKey); if (ids.length > 0) { - // Use the appropriate filter key for the current page params.set(filterKey, ids.join(",")); } diff --git a/ui/app/(prowler)/_overview/_components/provider-type-selector.tsx b/ui/app/(prowler)/_overview/_components/provider-type-selector.tsx index 0e18a0fac6..e5eb7928f3 100644 --- a/ui/app/(prowler)/_overview/_components/provider-type-selector.tsx +++ b/ui/app/(prowler)/_overview/_components/provider-type-selector.tsx @@ -143,7 +143,6 @@ export const ProviderTypeSelector = ({ // Clear account selection when changing provider types // User should manually select accounts if they want to filter by specific accounts params.delete("filter[provider_id__in]"); - params.delete("filter[provider__in]"); router.push(`?${params.toString()}`, { scroll: false }); }; diff --git a/ui/app/(prowler)/_overview/attack-surface/_components/attack-surface-card-item.tsx b/ui/app/(prowler)/_overview/attack-surface/_components/attack-surface-card-item.tsx index 9a26763c3e..46f2b0dd8e 100644 --- a/ui/app/(prowler)/_overview/attack-surface/_components/attack-surface-card-item.tsx +++ b/ui/app/(prowler)/_overview/attack-surface/_components/attack-surface-card-item.tsx @@ -2,7 +2,6 @@ import Link from "next/link"; import { AttackSurfaceItem } from "@/actions/overview"; import { Card, CardContent } from "@/components/shadcn"; -import { mapProviderFiltersForFindings } from "@/lib"; interface AttackSurfaceCardItemProps { item: AttackSurfaceItem; @@ -29,9 +28,6 @@ export function AttackSurfaceCardItem({ } }); - // Map provider filters for findings page compatibility - mapProviderFiltersForFindings(params); - return `/findings?${params.toString()}`; }; diff --git a/ui/app/(prowler)/_overview/graphs-tabs/findings-view/findings-view.ssr.tsx b/ui/app/(prowler)/_overview/graphs-tabs/findings-view/findings-view.ssr.tsx index 7dcbac50e9..c71e74c623 100644 --- a/ui/app/(prowler)/_overview/graphs-tabs/findings-view/findings-view.ssr.tsx +++ b/ui/app/(prowler)/_overview/graphs-tabs/findings-view/findings-view.ssr.tsx @@ -8,7 +8,6 @@ import { LinkToFindings } from "@/components/overview"; import { ColumnNewFindingsToDate } from "@/components/overview/new-findings-table/table/column-new-findings-to-date"; import { DataTable } from "@/components/ui/table"; import { createDict } from "@/lib/helper"; -import { mapProviderFiltersForFindingsObject } from "@/lib/provider-helpers"; import { FindingProps, SearchParamsProps } from "@/types"; import { pickFilterParams } from "../../_lib/filter-params"; @@ -27,8 +26,7 @@ export async function FindingsViewSSR({ searchParams }: FindingsViewSSRProps) { }; const filters = pickFilterParams(searchParams); - const mappedFilters = mapProviderFiltersForFindingsObject(filters); - const combinedFilters = { ...defaultFilters, ...mappedFilters }; + const combinedFilters = { ...defaultFilters, ...filters }; const findingsData = await getLatestFindings({ query: undefined, diff --git a/ui/app/(prowler)/_overview/graphs-tabs/risk-plot/risk-plot-client.tsx b/ui/app/(prowler)/_overview/graphs-tabs/risk-plot/risk-plot-client.tsx index dc312762ea..84405fe0f1 100644 --- a/ui/app/(prowler)/_overview/graphs-tabs/risk-plot/risk-plot-client.tsx +++ b/ui/app/(prowler)/_overview/graphs-tabs/risk-plot/risk-plot-client.tsx @@ -8,7 +8,6 @@ import { HorizontalBarChart } from "@/components/graphs/horizontal-bar-chart"; import { ScatterPlot } from "@/components/graphs/scatter-plot"; import { AlertPill } from "@/components/graphs/shared/alert-pill"; import type { BarDataPoint } from "@/components/graphs/types"; -import { mapProviderFiltersForFindings } from "@/lib/provider-helpers"; import { SEVERITY_FILTER_MAP } from "@/types/severities"; // Score color thresholds (0-100 scale, higher = better) @@ -42,9 +41,6 @@ export function RiskPlotClient({ data }: RiskPlotClientProps) { // Build the URL with current filters const params = new URLSearchParams(searchParams.toString()); - // Transform provider filters (provider_id__in -> provider__in) - mapProviderFiltersForFindings(params); - // Add severity filter const severity = SEVERITY_FILTER_MAP[dataPoint.name]; if (severity) { @@ -52,7 +48,7 @@ export function RiskPlotClient({ data }: RiskPlotClientProps) { } // Add provider filter for the selected point - params.set("filter[provider__in]", selectedPoint.providerId); + params.set("filter[provider_id__in]", selectedPoint.providerId); // Add exclude muted findings filter params.set("filter[muted]", "false"); diff --git a/ui/app/(prowler)/_overview/risk-severity/_components/risk-severity-chart.tsx b/ui/app/(prowler)/_overview/risk-severity/_components/risk-severity-chart.tsx index e718168698..9a8b11cd8f 100644 --- a/ui/app/(prowler)/_overview/risk-severity/_components/risk-severity-chart.tsx +++ b/ui/app/(prowler)/_overview/risk-severity/_components/risk-severity-chart.tsx @@ -5,7 +5,6 @@ import { useRouter, useSearchParams } from "next/navigation"; import { HorizontalBarChart } from "@/components/graphs/horizontal-bar-chart"; import { BarDataPoint } from "@/components/graphs/types"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/shadcn"; -import { mapProviderFiltersForFindings } from "@/lib/provider-helpers"; import { calculatePercentage } from "@/lib/utils"; import { SEVERITY_FILTER_MAP } from "@/types/severities"; @@ -31,8 +30,6 @@ export const RiskSeverityChart = ({ // Build the URL with current filters plus severity and muted const params = new URLSearchParams(searchParams.toString()); - mapProviderFiltersForFindings(params); - const severity = SEVERITY_FILTER_MAP[dataPoint.name]; if (severity) { params.set("filter[severity__in]", severity); diff --git a/ui/app/(prowler)/_overview/severity-over-time/_components/finding-severity-over-time.tsx b/ui/app/(prowler)/_overview/severity-over-time/_components/finding-severity-over-time.tsx index 3990f52b31..619e8631fd 100644 --- a/ui/app/(prowler)/_overview/severity-over-time/_components/finding-severity-over-time.tsx +++ b/ui/app/(prowler)/_overview/severity-over-time/_components/finding-severity-over-time.tsx @@ -66,7 +66,7 @@ export const FindingSeverityOverTime = ({ params.set("filter[provider_type__in]", providerType); } if (providerId) { - params.set("filter[provider__in]", providerId); + params.set("filter[provider_id__in]", providerId); } router.push(`/findings?${params.toString()}`); diff --git a/ui/app/(prowler)/_overview/status-chart/_components/status-chart.tsx b/ui/app/(prowler)/_overview/status-chart/_components/status-chart.tsx index 4615410d80..8f6540ecb4 100644 --- a/ui/app/(prowler)/_overview/status-chart/_components/status-chart.tsx +++ b/ui/app/(prowler)/_overview/status-chart/_components/status-chart.tsx @@ -13,7 +13,6 @@ import { CardVariant, ResourceStatsCard, } from "@/components/shadcn"; -import { mapProviderFiltersForFindings } from "@/lib/provider-helpers"; import { calculatePercentage } from "@/lib/utils"; interface FindingsData { total: number; @@ -39,8 +38,6 @@ export const StatusChart = ({ // Build the URL with current filters plus status and muted const params = new URLSearchParams(searchParams.toString()); - mapProviderFiltersForFindings(params); - // Add status filter based on which segment was clicked if (dataPoint.name === "Fail Findings") { params.set("filter[status__in]", "FAIL"); diff --git a/ui/app/(prowler)/_overview/watchlist/_components/service-watchlist.tsx b/ui/app/(prowler)/_overview/watchlist/_components/service-watchlist.tsx index 71fc7e2146..b39adfbd2b 100644 --- a/ui/app/(prowler)/_overview/watchlist/_components/service-watchlist.tsx +++ b/ui/app/(prowler)/_overview/watchlist/_components/service-watchlist.tsx @@ -4,7 +4,6 @@ import { useRouter, useSearchParams } from "next/navigation"; import { useState } from "react"; import { ServiceOverview } from "@/actions/overview"; -import { mapProviderFiltersForFindings } from "@/lib/provider-helpers"; import { SortToggleButton } from "./sort-toggle-button"; import { WatchlistCard, WatchlistItem } from "./watchlist-card"; @@ -29,9 +28,6 @@ export const ServiceWatchlist = ({ items }: { items: ServiceOverview[] }) => { const handleItemClick = (item: WatchlistItem) => { const params = new URLSearchParams(searchParams.toString()); - - mapProviderFiltersForFindings(params); - params.set("filter[service__in]", item.key); params.set("filter[status__in]", "FAIL"); router.push(`/findings?${params.toString()}`); diff --git a/ui/components/graphs/sankey-chart.tsx b/ui/components/graphs/sankey-chart.tsx index a562f6a823..fe7708e7a1 100644 --- a/ui/components/graphs/sankey-chart.tsx +++ b/ui/components/graphs/sankey-chart.tsx @@ -7,7 +7,6 @@ import { Rectangle, ResponsiveContainer, Sankey, Tooltip } from "recharts"; import { PROVIDER_ICONS } from "@/components/icons/providers-badge"; import { initializeChartColors } from "@/lib/charts/colors"; -import { mapProviderFiltersForFindings } from "@/lib/provider-helpers"; import { PROVIDER_DISPLAY_NAMES } from "@/types/providers"; import { SEVERITY_FILTER_MAP } from "@/types/severities"; @@ -463,9 +462,6 @@ export function SankeyChart({ const severityFilter = SEVERITY_FILTER_MAP[nodeName]; if (severityFilter) { const params = new URLSearchParams(searchParams.toString()); - - mapProviderFiltersForFindings(params); - params.set("filter[severity__in]", severityFilter); params.set("filter[status__in]", "FAIL"); params.set("filter[muted]", "false"); @@ -480,12 +476,10 @@ export function SankeyChart({ if (severityFilter) { const params = new URLSearchParams(searchParams.toString()); - mapProviderFiltersForFindings(params); - // Always set provider_type filter based on the clicked link's source (provider) // This ensures clicking "AWS → High" filters by AWS even when no global filter is set - const hasProviderIdFilter = searchParams.has("filter[provider_id__in]"); - if (providerType && !hasProviderIdFilter) { + const hasProviderFilter = searchParams.has("filter[provider_id__in]"); + if (providerType && !hasProviderFilter) { params.set("filter[provider_type__in]", providerType); } diff --git a/ui/components/graphs/threat-map.tsx b/ui/components/graphs/threat-map.tsx index f103ef35c2..498a2bf5c3 100644 --- a/ui/components/graphs/threat-map.tsx +++ b/ui/components/graphs/threat-map.tsx @@ -12,7 +12,6 @@ import { useRouter, useSearchParams } from "next/navigation"; import { useEffect, useRef, useState } from "react"; import { Card } from "@/components/shadcn/card/card"; -import { mapProviderFiltersForFindings } from "@/lib/provider-helpers"; import { HorizontalBarChart } from "./horizontal-bar-chart"; import { @@ -399,7 +398,6 @@ export function ThreatMap({ providerType?: string, ) => { const params = new URLSearchParams(searchParams.toString()); - mapProviderFiltersForFindings(params); if (providerType) params.set("filter[provider_type__in]", providerType); params.set("filter[region__in]", regionCode); params.set("filter[status__in]", status); diff --git a/ui/lib/provider-helpers.ts b/ui/lib/provider-helpers.ts index bb6336701a..3f5d9ceae2 100644 --- a/ui/lib/provider-helpers.ts +++ b/ui/lib/provider-helpers.ts @@ -5,46 +5,6 @@ import { ProviderType, } from "@/types/providers"; -/** - * Maps overview provider filters to findings page provider filters. - * Converts provider_id__in to provider__in and removes provider_type__in - * since provider__in is more specific. - */ -export const mapProviderFiltersForFindings = ( - params: URLSearchParams, -): void => { - const providerIds = params.get("filter[provider_id__in]"); - if (providerIds) { - params.delete("filter[provider_id__in]"); - params.delete("filter[provider_type__in]"); - params.set("filter[provider__in]", providerIds); - } -}; - -/** - * Maps overview provider filters to findings page provider filters (object version). - * Converts provider_id__in to provider__in and removes provider_type__in - * since provider__in is more specific. - */ -export const mapProviderFiltersForFindingsObject = < - T extends Record, ->( - filters: T, -): T => { - const result = { ...filters }; - const providerIdKey = "filter[provider_id__in]"; - const providerTypeKey = "filter[provider_type__in]"; - const providerKey = "filter[provider__in]"; - - if (providerIdKey in result) { - result[providerKey as keyof T] = result[providerIdKey as keyof T]; - delete result[providerIdKey as keyof T]; - delete result[providerTypeKey as keyof T]; - } - - return result; -}; - export const extractProviderUIDs = ( providersData: ProvidersApiResponse, ): string[] => {