Back to Checks
aws

Bedrock guardrail blocks ungrounded and irrelevant model responses

bedrock_guardrail_contextual_grounding_filter_enabled
Service: bedrockSeverity: high
OfficialBuilt-inVerified
Categories:gen-ai
Resource Type:Other

Bedrock guardrails can attach a contextualGroundingPolicy whose filters score each response for GROUNDING (supported by the retrieved source) and RELEVANCE (answers the question asked). Both filter types must be enabled, set to action: BLOCK, and carry a threshold above 0.

Risk

Without both filters the model can return confident, unsupported answers that a RAG application presents as sourced fact.

Three settings make a configured filter inert: enabled: false runs no evaluation, so a BLOCK action and tuned threshold sit unused; action: NONE scores the response and reports it in the trace while still delivering it; a threshold of 0 can never be tripped.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks bedrock_guardrail_contextual_grounding_filter_enabled

Recommendation

Configure both a GROUNDING and a RELEVANCE contextual grounding filter on every guardrail used by a retrieval-augmented application, set each action to BLOCK, and tune each threshold above 0 against a labelled sample rather than leaving it at the minimum.

Remediation

CLI

aws bedrock update-guardrail --guardrail-identifier example-guardrail-id --name example-guardrail --blocked-input-messaging "Sorry, I cannot answer that." --blocked-outputs-messaging "Sorry, I cannot answer that." --contextual-grounding-policy-config '{"filtersConfig":[{"type":"GROUNDING","threshold":0.75,"action":"BLOCK","enabled":true},{"type":"RELEVANCE","threshold":0.75,"action":"BLOCK","enabled":true}]}'

Terraform

resource "awscc_bedrock_guardrail" "example_resource" {
  name                      = "example-guardrail"
  blocked_input_messaging   = "Sorry, I cannot answer that."
  blocked_outputs_messaging = "Sorry, I cannot answer that."

  contextual_grounding_policy_config = {
    filters_config = [
      {
        type      = "GROUNDING"
        threshold = 0.75 # Critical: 0 can never be tripped
        action    = "BLOCK"  # Critical: NONE scores without blocking
        enabled   = true     # Critical: when false the evaluation never runs
      },
      {
        type      = "RELEVANCE"
        threshold = 0.75
        action    = "BLOCK"
        enabled   = true
      },
    ]
  }
}

NativeIaC

Resources:
  ExampleResource:
    Type: AWS::Bedrock::Guardrail
    Properties:
      Name: example_resource
      BlockedInputMessaging: Sorry, I cannot answer that.
      BlockedOutputsMessaging: Sorry, I cannot answer that.
      ContextualGroundingPolicyConfig:
        FiltersConfig:
          - Type: GROUNDING
            Threshold: 0.75
            Action: BLOCK  # Critical: NONE only reports, it does not block
            Enabled: true  # Critical: false runs no evaluation at all
          - Type: RELEVANCE
            Threshold: 0.75
            Action: BLOCK
            Enabled: true

Other

1Open the AWS Console and go to Amazon Bedrock
2Select **Guardrails**, then the guardrail
3Edit the contextual grounding check
4Enable both the grounding and the relevance filter
5Set each threshold above 0 to a value tuned for your corpus
6Set each action to Block rather than None
7Save and create a new guardrail version

Check Code

from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.bedrock.bedrock_client import bedrock_client

REQUIRED_FILTER_TYPES = frozenset({"GROUNDING", "RELEVANCE"})
BLOCKING_ACTION = "BLOCK"


class bedrock_guardrail_contextual_grounding_filter_enabled(Check):
    """Ensure Bedrock guardrails block ungrounded and irrelevant model responses.

    - PASS: The guardrail configures both a GROUNDING and a RELEVANCE
      contextual grounding filter, each enabled, with action BLOCK and a
      threshold above 0.
    - FAIL: No contextual grounding policy is configured; one of the two
      required filter types is missing; a present filter carries
      enabled: false, so its evaluation never runs; a present filter uses
      action NONE, which scores and reports without blocking; or a present
      filter has a threshold of 0, which nothing can ever trip.
    - MANUAL: GetGuardrail failed, so the policy could not be retrieved and
      compliance cannot be asserted from an absent field; or a filter is
      otherwise compliant but omits enabled or action, leaving it unknown
      whether the evaluation runs or whether it blocks; or ListGuardrails failed
      for a Region, so that Region's guardrails are unknown rather than absent.

    enabled and action are both optional members of
    GuardrailContextualGroundingFilter (only type and threshold are required)
    and AWS documents no default for either, so an omitted value is unknown
    rather than false or NONE. Reading an omitted enabled as false would FAIL
    filters that are in fact evaluating; reading an omitted action as NONE
    would assert a misconfiguration the response never stated, and would print
    the literal None into status_extended where it reads as an AWS enum value.
    Both are therefore reported MANUAL, matching how this check already treats
    a guardrail whose detail could not be retrieved. An explicit enabled: false
    or an explicit non-BLOCK action is a definite finding and still FAILs, as
    does a zero threshold, so an unknown never masks a real one.
    """

    def execute(self) -> list[Check_Report_AWS]:
        """Execute the check logic.

        Returns:
            A list of reports containing the result of the check.
        """
        findings = []

        for region, error in sorted(bedrock_client.guardrails_scan_errors.items()):
            report = Check_Report_AWS(
                metadata=self.metadata(), resource={"region": region}
            )
            report.region = region
            report.resource_id = "guardrail/unknown"
            report.resource_arn = f"arn:{bedrock_client.audited_partition}:bedrock:{region}:{bedrock_client.audited_account}:guardrail/unknown"
            report.status = "MANUAL"
            report.status_extended = f"Bedrock guardrails could not be listed in region {region} ({error}); verify manually that each one blocks ungrounded and irrelevant responses."
            findings.append(report)

        for guardrail in bedrock_client.guardrails.values():
            report = Check_Report_AWS(metadata=self.metadata(), resource=guardrail)

            if not guardrail.detail_retrieved:
                # GetGuardrail failed (permissions, throttling, transient
                # error). An absent policy is not evidence of a missing one.
                report.status = "MANUAL"
                report.status_extended = f"Bedrock Guardrail {guardrail.name} contextual grounding policy could not be retrieved in region {guardrail.region}; verify manually that GROUNDING and RELEVANCE filters block ungrounded responses."
                findings.append(report)
                continue

            filters_by_type = {
                filter.type: filter
                for filter in guardrail.contextual_grounding_filters
                if filter.type
            }

            if not filters_by_type:
                report.status = "FAIL"
                report.status_extended = f"Bedrock Guardrail {guardrail.name} has no contextual grounding policy configured in region {guardrail.region}, so ungrounded and irrelevant responses are never detected."
                findings.append(report)
                continue

            missing_types = sorted(REQUIRED_FILTER_TYPES - set(filters_by_type))
            if missing_types:
                report.status = "FAIL"
                # Both required types can be missing at once, so the nouns follow
                # the count rather than assuming a single filter.
                noun = "filter" if len(missing_types) == 1 else "filters"
                classes = "that class" if len(missing_types) == 1 else "those classes"
                report.status_extended = f"Bedrock Guardrail {guardrail.name} contextual grounding policy is missing the {', '.join(missing_types)} {noun} in region {guardrail.region}, leaving {classes} of ungrounded response unchecked."
                findings.append(report)
                continue

            reasons = []
            unknown_types = []
            for filter_type in sorted(REQUIRED_FILTER_TYPES):
                filter = filters_by_type[filter_type]
                action = filter.action
                if filter.enabled is False:
                    reasons.append(
                        f"the {filter_type} filter is disabled, so its evaluation never runs regardless of its action or threshold"
                    )
                elif action is not None and action != BLOCKING_ACTION:
                    reasons.append(
                        f"the {filter_type} filter uses action {action} instead of BLOCK, so it scores and reports without blocking"
                    )
                elif not (filter.threshold or 0) > 0:
                    reasons.append(
                        f"the {filter_type} filter has a threshold of {filter.threshold}, which no response can ever trip"
                    )
                elif action is None or filter.enabled is None:
                    # Name only the attributes actually omitted: one of the two
                    # may be present, and claiming both are missing describes a
                    # response the guardrail did not return.
                    missing = [
                        attribute
                        for attribute, value in (
                            ("enabled", filter.enabled),
                            ("action", action),
                        )
                        if value is None
                    ]
                    unknown_types.append(
                        f"{filter_type} filter omits {' and '.join(missing)}"
                    )

            if reasons:
                report.status = "FAIL"
                report.status_extended = f"Bedrock Guardrail {guardrail.name} does not block ungrounded responses in region {guardrail.region}: {'; '.join(reasons)}."
            elif unknown_types:
                report.status = "MANUAL"
                subject = "it blocks" if len(unknown_types) == 1 else "they block"
                report.status_extended = f"Bedrock Guardrail {guardrail.name} has both required contextual grounding filters with a non-zero threshold in region {guardrail.region}, but the {', '.join(unknown_types)}, so whether {subject} is unknown; verify manually that the evaluation runs and blocks."
            else:
                report.status = "PASS"
                report.status_extended = f"Bedrock Guardrail {guardrail.name} blocks ungrounded and irrelevant responses with GROUNDING and RELEVANCE filters in region {guardrail.region}."
            findings.append(report)

        return findings