Skip to content

Commit 921f49a

Browse files
feat(aws): add bedrock_prompt_management_exists security check (#10878)
1 parent 6cb770f commit 921f49a

9 files changed

Lines changed: 535 additions & 0 deletions

File tree

prowler/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
1010
- Universal compliance pipeline integrated into the CLI: `--list-compliance` and `--list-compliance-requirements` show universal frameworks, and CSV plus OCSF outputs are generated for any framework declaring a `TableConfig` [(#10301)](https://github.com/prowler-cloud/prowler/pull/10301)
1111
- ASD Essential Eight Maturity Model compliance framework for AWS (Maturity Level One, Nov 2023) [(#10808)](https://github.com/prowler-cloud/prowler/pull/10808)
1212
- Update Vercel checks to return personalized finding status extended depending on billing plan and classify them with billing-plan categories [(#10663)](https://github.com/prowler-cloud/prowler/pull/10663)
13+
- `bedrock_prompt_management_exists` check for AWS provider [(#10878)](https://github.com/prowler-cloud/prowler/pull/10878)
1314

1415
### 🔄 Changed
1516

prowler/compliance/aws/kisa_isms_p_2023_aws.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2897,6 +2897,7 @@
28972897
"bedrock_guardrails_configured",
28982898
"bedrock_model_invocation_logging_enabled",
28992899
"bedrock_model_invocation_logs_encryption_enabled",
2900+
"bedrock_prompt_management_exists",
29002901
"cloudformation_stack_outputs_find_secrets",
29012902
"cloudfront_distributions_custom_ssl_certificate",
29022903
"cloudfront_distributions_default_root_object",

prowler/compliance/aws/kisa_isms_p_2023_korean_aws.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2901,6 +2901,7 @@
29012901
"bedrock_guardrails_configured",
29022902
"bedrock_model_invocation_logging_enabled",
29032903
"bedrock_model_invocation_logs_encryption_enabled",
2904+
"bedrock_prompt_management_exists",
29042905
"cloudformation_stack_outputs_find_secrets",
29052906
"cloudfront_distributions_custom_ssl_certificate",
29062907
"cloudfront_distributions_default_root_object",

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

Whitespace-only changes.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"Provider": "aws",
3+
"CheckID": "bedrock_prompt_management_exists",
4+
"CheckTitle": "Amazon Bedrock Prompt Management prompts exist in the region",
5+
"CheckType": [
6+
"Software and Configuration Checks/AWS Security Best Practices"
7+
],
8+
"ServiceName": "bedrock",
9+
"SubServiceName": "",
10+
"ResourceIdTemplate": "",
11+
"Severity": "low",
12+
"ResourceType": "Other",
13+
"ResourceGroup": "ai_ml",
14+
"Description": "**Bedrock Prompt Management** enables centralized creation, versioning, and governance of prompts used with foundation models.\n\nThis region-level check verifies whether at least one managed prompt exists in each scanned region, used as an adoption signal for Prompt Management. The presence of a prompt does not by itself guarantee that every application prompt is managed.",
15+
"Risk": "Without **Prompt Management**, prompts are scattered across applications with no central oversight, versioning, or auditability over instructions sent to foundation models, weakening governance and compliance posture.\n\nManaged prompts are a governance enabler; **prompt injection** defenses are provided by Bedrock **guardrails**, covered by separate checks.",
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 create-prompt --name example_prompt --default-variant default --variants '[{\"name\":\"default\",\"templateType\":\"TEXT\",\"templateConfiguration\":{\"text\":{\"text\":\"Your prompt template here.\"}}}]'",
24+
"NativeIaC": "",
25+
"Other": "1. Open the Amazon Bedrock console\n2. Navigate to Prompt Management\n3. Click Create prompt\n4. Provide a name and configure the prompt template (a prompt can contain at most one variant; additional variants are created via CreatePromptVersion)\n5. Save the prompt",
26+
"Terraform": ""
27+
},
28+
"Recommendation": {
29+
"Text": "Adopt **Bedrock Prompt Management** to centralize prompt definitions, enforce versioning, and maintain governance over model interactions.\n\nUse managed prompts with **guardrails** and apply **least privilege** access controls to restrict who can create or modify prompts.",
30+
"Url": "https://hub.prowler.com/check/bedrock_prompt_management_exists"
31+
}
32+
},
33+
"Categories": [
34+
"gen-ai"
35+
],
36+
"DependsOn": [],
37+
"RelatedTo": [],
38+
"Notes": "Results are generated per scanned region. Regions where `ListPrompts` cannot be queried are omitted from the findings."
39+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""Check for region-level Bedrock Prompt Management adoption."""
2+
3+
from prowler.lib.check.models import Check, Check_Report_AWS
4+
from prowler.providers.aws.services.bedrock.bedrock_agent_client import (
5+
bedrock_agent_client,
6+
)
7+
8+
9+
class bedrock_prompt_management_exists(Check):
10+
"""Check whether Amazon Bedrock Prompt Management prompts exist in the region.
11+
12+
A region is reported only when ListPrompts succeeded for it; regions where
13+
the API call failed (e.g. AccessDenied, unsupported region) are skipped at
14+
the service layer and produce no finding.
15+
16+
- PASS: At least one managed prompt exists in the region (one finding per prompt).
17+
- FAIL: No managed prompts exist in the region (one finding per region).
18+
"""
19+
20+
def execute(self) -> list[Check_Report_AWS]:
21+
"""Execute the Bedrock Prompt Management exists check.
22+
23+
Returns:
24+
A list of reports containing the result of the check.
25+
"""
26+
findings = []
27+
for region in sorted(bedrock_agent_client.prompt_scanned_regions):
28+
regional_prompts = sorted(
29+
(
30+
prompt
31+
for prompt in bedrock_agent_client.prompts.values()
32+
if prompt.region == region
33+
),
34+
key=lambda prompt: prompt.name,
35+
)
36+
37+
if regional_prompts:
38+
for prompt in regional_prompts:
39+
report = Check_Report_AWS(metadata=self.metadata(), resource=prompt)
40+
report.status = "PASS"
41+
report.status_extended = f"Bedrock Prompt Management prompt {prompt.name} exists in region {region}."
42+
findings.append(report)
43+
else:
44+
report = Check_Report_AWS(metadata=self.metadata(), resource={})
45+
report.region = region
46+
report.resource_id = "prompt-management"
47+
report.resource_arn = f"arn:{bedrock_agent_client.audited_partition}:bedrock:{region}:{bedrock_agent_client.audited_account}:prompt-management"
48+
report.status = "FAIL"
49+
report.status_extended = (
50+
f"No Bedrock Prompt Management prompts exist in region {region}."
51+
)
52+
findings.append(report)
53+
54+
return findings

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,10 @@ def __init__(self, provider):
140140
# Call AWSService's __init__
141141
super().__init__("bedrock-agent", provider)
142142
self.agents = {}
143+
self.prompts = {}
144+
self.prompt_scanned_regions: set = set()
143145
self.__threading_call__(self._list_agents)
146+
self.__threading_call__(self._list_prompts)
144147
self.__threading_call__(self._list_tags_for_resource, self.agents.values())
145148

146149
def _list_agents(self, regional_client):
@@ -167,7 +170,32 @@ def _list_agents(self, regional_client):
167170
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
168171
)
169172

173+
def _list_prompts(self, regional_client):
174+
"""List all prompts in a region.
175+
176+
Prompt Management is evaluated as a region-level adoption signal, so
177+
prompt collection is intentionally not filtered by audit_resources.
178+
"""
179+
logger.info("Bedrock Agent - Listing Prompts...")
180+
try:
181+
paginator = regional_client.get_paginator("list_prompts")
182+
for page in paginator.paginate():
183+
for prompt in page.get("promptSummaries", []):
184+
prompt_arn = prompt.get("arn", "")
185+
self.prompts[prompt_arn] = Prompt(
186+
id=prompt.get("id", ""),
187+
name=prompt.get("name", ""),
188+
arn=prompt_arn,
189+
region=regional_client.region,
190+
)
191+
self.prompt_scanned_regions.add(regional_client.region)
192+
except Exception as error:
193+
logger.error(
194+
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
195+
)
196+
170197
def _list_tags_for_resource(self, resource):
198+
"""List tags for a Bedrock Agent resource."""
171199
logger.info("Bedrock Agent - Listing Tags for Resource...")
172200
try:
173201
agent_tags = (
@@ -190,3 +218,12 @@ class Agent(BaseModel):
190218
guardrail_id: Optional[str] = None
191219
region: str
192220
tags: Optional[list] = []
221+
222+
223+
class Prompt(BaseModel):
224+
"""Model representing a Bedrock Prompt Management prompt."""
225+
226+
id: str
227+
name: str
228+
arn: str
229+
region: str

0 commit comments

Comments
 (0)