Skip to content

Commit 2854a74

Browse files
feat(aws): add bedrock_prompt_have_multiple_variants security check
Add new security check bedrock_prompt_have_multiple_variants for aws provider. Includes check implementation, metadata, and unit tests.
1 parent e252058 commit 2854a74

10 files changed

Lines changed: 377 additions & 0 deletions

File tree

prowler/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
66

77
### 🚀 Added
88

9+
- `bedrock_prompt_have_multiple_variants` check for aws provider [(#10905)](https://github.com/prowler-cloud/prowler/pull/10905)
910
- `--repo-list-file` CLI flag for GitHub provider to load repositories from a file [(#10501)](https://github.com/prowler-cloud/prowler/pull/10501)
1011
- SARIF output format for the IaC provider, enabling GitHub Code Scanning integration via `--output-formats sarif` [(#10626)](https://github.com/prowler-cloud/prowler/pull/10626)
1112
- `repository_default_branch_dismisses_stale_reviews` check for GitHub provider to ensure stale pull request approvals are dismissed when new commits are pushed [(#10569)](https://github.com/prowler-cloud/prowler/pull/10569)

prowler/compliance/aws/iso27001_2022_aws.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,7 @@
12401240
"Checks": [
12411241
"autoscaling_group_capacity_rebalance_enabled",
12421242
"autoscaling_group_multiple_az",
1243+
"bedrock_prompt_have_multiple_variants",
12431244
"cloudfront_distributions_multiple_origin_failover_configured",
12441245
"directconnect_connection_redundancy",
12451246
"directconnect_virtual_interface_redundancy",

prowler/compliance/aws/kisa_isms_p_2023_aws.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,6 +2385,7 @@
23852385
"autoscaling_group_multiple_az",
23862386
"autoscaling_group_multiple_instance_types",
23872387
"awslambda_function_vpc_multi_az",
2388+
"bedrock_prompt_have_multiple_variants",
23882389
"cloudformation_stacks_termination_protection_enabled",
23892390
"cloudfront_distributions_s3_origin_non_existent_bucket",
23902391
"directconnect_connection_redundancy",

prowler/compliance/aws/kisa_isms_p_2023_korean_aws.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2389,6 +2389,7 @@
23892389
"autoscaling_group_multiple_az",
23902390
"autoscaling_group_multiple_instance_types",
23912391
"awslambda_function_vpc_multi_az",
2392+
"bedrock_prompt_have_multiple_variants",
23922393
"cloudformation_stacks_termination_protection_enabled",
23932394
"cloudfront_distributions_s3_origin_non_existent_bucket",
23942395
"directconnect_connection_redundancy",

prowler/compliance/aws/nist_csf_2.0_aws.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,6 +1389,7 @@
13891389
"elb_cross_zone_load_balancing_enabled",
13901390
"autoscaling_group_capacity_rebalance_enabled",
13911391
"autoscaling_group_multiple_az",
1392+
"bedrock_prompt_have_multiple_variants",
13921393
"vpc_vpn_connection_tunnels_up",
13931394
"cloudfront_distributions_multiple_origin_failover_configured",
13941395
"s3_bucket_cross_region_replication"

prowler/providers/aws/services/bedrock/bedrock_prompt_have_multiple_variants/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"Provider": "aws",
3+
"CheckID": "bedrock_prompt_have_multiple_variants",
4+
"CheckTitle": "Bedrock prompt ensures resilience through multiple configured variants for A/B testing",
5+
"CheckType": [
6+
"Software and Configuration Checks/AWS Security Best Practices"
7+
],
8+
"ServiceName": "bedrock",
9+
"SubServiceName": "",
10+
"ResourceIdTemplate": "",
11+
"Severity": "medium",
12+
"ResourceType": "Other",
13+
"ResourceGroup": "ai_ml",
14+
"Description": "Bedrock prompts should have **multiple variants** configured to support A/B testing and improve resilience. Each variant can use different models, templates, or inference parameters, enabling prompt optimization and fallback strategies.",
15+
"Risk": "A prompt with a **single variant** lacks fallback options and cannot support A/B testing:\n- **Availability**: if the underlying model experiences degradation, no alternative variant is available\n- **Optimization**: inability to compare prompt performance across different configurations\n- **Resilience**: single point of failure in prompt execution pipelines",
16+
"RelatedUrl": "",
17+
"AdditionalURLs": [
18+
"https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-management.html",
19+
"https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-management-create.html"
20+
],
21+
"Remediation": {
22+
"Code": {
23+
"CLI": "aws bedrock-agent update-prompt --prompt-identifier <prompt_id> --name <prompt_name> --variants '[{\"name\":\"variant-1\",\"modelId\":\"<model_id_1>\",\"templateType\":\"TEXT\",\"templateConfiguration\":{\"text\":{\"text\":\"<template>\"}}},{\"name\":\"variant-2\",\"modelId\":\"<model_id_2>\",\"templateType\":\"TEXT\",\"templateConfiguration\":{\"text\":{\"text\":\"<template>\"}}}]'",
24+
"NativeIaC": "",
25+
"Other": "1. Open the Amazon Bedrock console\n2. Navigate to Prompt management\n3. Select the prompt\n4. Add a new variant with a different model or configuration\n5. Save the prompt",
26+
"Terraform": ""
27+
},
28+
"Recommendation": {
29+
"Text": "Configure **multiple variants** for each Bedrock prompt to enable A/B testing and improve resilience. Use different models, inference parameters, or prompt templates across variants to optimize performance and ensure fallback options are available.",
30+
"Url": "https://hub.prowler.com/check/bedrock_prompt_have_multiple_variants"
31+
}
32+
},
33+
"Categories": [
34+
"gen-ai",
35+
"resilience"
36+
],
37+
"DependsOn": [],
38+
"RelatedTo": [],
39+
"Notes": ""
40+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from prowler.lib.check.models import Check, Check_Report_AWS
2+
from prowler.providers.aws.services.bedrock.bedrock_agent_client import (
3+
bedrock_agent_client,
4+
)
5+
6+
7+
class bedrock_prompt_have_multiple_variants(Check):
8+
"""Ensure that Bedrock prompts have multiple variants configured.
9+
10+
This check evaluates whether each Bedrock prompt has more than one variant
11+
configured to enable A/B testing and improved resilience.
12+
- PASS: The Bedrock prompt has multiple variants configured.
13+
- FAIL: The Bedrock prompt has fewer than 2 variants configured.
14+
"""
15+
16+
def execute(self) -> list[Check_Report_AWS]:
17+
"""Execute the Bedrock prompt multiple variants check.
18+
19+
Returns:
20+
A list of reports containing the result of the check.
21+
"""
22+
findings = []
23+
for prompt in bedrock_agent_client.prompts.values():
24+
report = Check_Report_AWS(metadata=self.metadata(), resource=prompt)
25+
report.status = "FAIL"
26+
num_variants = len(prompt.variants)
27+
report.status_extended = f"Bedrock Prompt {prompt.name} has only {num_variants} variant{'s' if num_variants != 1 else ''} configured, multiple variants are recommended for A/B testing and resilience."
28+
if num_variants > 1:
29+
report.status = "PASS"
30+
report.status_extended = f"Bedrock Prompt {prompt.name} has {num_variants} variants configured for A/B testing and resilience."
31+
findings.append(report)
32+
return findings

prowler/providers/aws/services/bedrock/bedrock_service.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,17 @@ class Guardrail(BaseModel):
122122

123123

124124
class BedrockAgent(AWSService):
125+
"""Bedrock Agent service class for managing agents and prompts."""
126+
125127
def __init__(self, provider):
128+
"""Initialize the BedrockAgent service."""
126129
# Call AWSService's __init__
127130
super().__init__("bedrock-agent", provider)
128131
self.agents = {}
132+
self.prompts = {}
129133
self.__threading_call__(self._list_agents)
134+
self.__threading_call__(self._list_prompts)
135+
self.__threading_call__(self._get_prompt, self.prompts.values())
130136
self.__threading_call__(self._list_tags_for_resource, self.agents.values())
131137

132138
def _list_agents(self, regional_client):
@@ -153,7 +159,43 @@ def _list_agents(self, regional_client):
153159
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
154160
)
155161

162+
def _list_prompts(self, regional_client):
163+
"""List all Bedrock prompts in a region."""
164+
logger.info("Bedrock Agent - Listing Prompts...")
165+
try:
166+
paginator = regional_client.get_paginator("list_prompts")
167+
for page in paginator.paginate():
168+
for prompt in page.get("promptSummaries", []):
169+
prompt_arn = prompt["arn"]
170+
if not self.audit_resources or (
171+
is_resource_filtered(prompt_arn, self.audit_resources)
172+
):
173+
self.prompts[prompt_arn] = Prompt(
174+
id=prompt["id"],
175+
name=prompt["name"],
176+
arn=prompt_arn,
177+
region=regional_client.region,
178+
)
179+
except Exception as error:
180+
logger.error(
181+
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
182+
)
183+
184+
def _get_prompt(self, prompt):
185+
"""Get detailed prompt information including variants."""
186+
logger.info("Bedrock Agent - Getting Prompt...")
187+
try:
188+
prompt_info = self.regional_clients[prompt.region].get_prompt(
189+
promptIdentifier=prompt.id
190+
)
191+
prompt.variants = prompt_info.get("variants", [])
192+
except Exception as error:
193+
logger.error(
194+
f"{prompt.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
195+
)
196+
156197
def _list_tags_for_resource(self, resource):
198+
"""List tags for a Bedrock Agent resource."""
157199
logger.info("Bedrock Agent - Listing Tags for Resource...")
158200
try:
159201
agent_tags = (
@@ -170,9 +212,22 @@ def _list_tags_for_resource(self, resource):
170212

171213

172214
class Agent(BaseModel):
215+
"""Model for a Bedrock Agent resource."""
216+
173217
id: str
174218
name: str
175219
arn: str
176220
guardrail_id: Optional[str] = None
177221
region: str
178222
tags: Optional[list] = []
223+
224+
225+
class Prompt(BaseModel):
226+
"""Model for a Bedrock Prompt resource."""
227+
228+
id: str
229+
name: str
230+
arn: str
231+
region: str
232+
variants: Optional[list] = []
233+
tags: Optional[list] = []

0 commit comments

Comments
 (0)