mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 20:42:02 +00:00
feat(aws): add cloudtrail_bedrock_logging_enabled security check (#10858)
This commit is contained in:
committed by
GitHub
parent
0b26c1a39c
commit
759f7b84d6
@@ -7,6 +7,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
### 🚀 Added
|
||||
|
||||
- `iam_user_access_not_stale_to_sagemaker` check for aws provider with configurable `max_unused_sagemaker_access_days` (default 90) [(#11000)](https://github.com/prowler-cloud/prowler/pull/11000)
|
||||
- `cloudtrail_bedrock_logging_enabled` check for AWS provider [(#10858)](https://github.com/prowler-cloud/prowler/pull/10858)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -550,6 +550,7 @@
|
||||
"apigatewayv2_api_access_logging_enabled",
|
||||
"awslambda_function_invoke_api_operations_cloudtrail_logging_enabled",
|
||||
"cloudfront_distributions_logging_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"cloudtrail_logs_s3_bucket_access_logging_enabled",
|
||||
"directoryservice_directory_log_forwarding_enabled",
|
||||
|
||||
@@ -3461,6 +3461,7 @@
|
||||
],
|
||||
"Checks": [
|
||||
"kinesis_stream_data_retention_period",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_multi_region_enabled_logging_management_events"
|
||||
]
|
||||
},
|
||||
@@ -3669,6 +3670,7 @@
|
||||
"awslambda_function_invoke_api_operations_cloudtrail_logging_enabled",
|
||||
"bedrock_model_invocation_logging_enabled",
|
||||
"cloudfront_distributions_logging_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"cloudtrail_logs_s3_bucket_access_logging_enabled",
|
||||
"cloudtrail_multi_region_enabled_logging_management_events",
|
||||
|
||||
@@ -1958,6 +1958,7 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_multi_region_enabled_logging_management_events",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
|
||||
@@ -5857,6 +5857,7 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_multi_region_enabled_logging_management_events",
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
|
||||
@@ -439,6 +439,7 @@
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
"cloudtrail_s3_dataevents_write_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"elbv2_logging_enabled",
|
||||
"elb_logging_enabled",
|
||||
@@ -594,6 +595,7 @@
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
"cloudtrail_s3_dataevents_write_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"elbv2_logging_enabled",
|
||||
"elb_logging_enabled",
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
],
|
||||
"Checks": [
|
||||
"apigateway_restapi_logging_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
"cloudtrail_s3_dataevents_write_enabled",
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
],
|
||||
"Checks": [
|
||||
"apigateway_restapi_logging_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
"cloudtrail_s3_dataevents_write_enabled",
|
||||
@@ -632,6 +633,7 @@
|
||||
],
|
||||
"Checks": [
|
||||
"apigateway_restapi_logging_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
"cloudtrail_s3_dataevents_write_enabled",
|
||||
|
||||
@@ -1294,6 +1294,7 @@
|
||||
"bedrock_model_invocation_logging_enabled",
|
||||
"bedrock_model_invocation_logs_encryption_enabled",
|
||||
"cloudfront_distributions_logging_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"cloudtrail_kms_encryption_enabled",
|
||||
"cloudtrail_log_file_validation_enabled",
|
||||
|
||||
@@ -2540,6 +2540,7 @@
|
||||
"bedrock_model_invocation_logging_enabled",
|
||||
"bedrock_model_invocation_logs_encryption_enabled",
|
||||
"cloudfront_distributions_logging_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_bucket_requires_mfa_delete",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"cloudtrail_insights_exist",
|
||||
|
||||
@@ -1604,6 +1604,7 @@
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
"cloudtrail_s3_dataevents_write_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"elbv2_logging_enabled",
|
||||
"elb_logging_enabled",
|
||||
@@ -2162,6 +2163,7 @@
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
"cloudtrail_s3_dataevents_write_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"elbv2_logging_enabled",
|
||||
"elb_logging_enabled",
|
||||
@@ -2189,6 +2191,7 @@
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
"cloudtrail_s3_dataevents_write_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"elbv2_logging_enabled",
|
||||
"elb_logging_enabled",
|
||||
|
||||
@@ -1312,6 +1312,7 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_kms_encryption_enabled",
|
||||
"cloudtrail_log_file_validation_enabled",
|
||||
"cloudtrail_logs_s3_bucket_access_logging_enabled",
|
||||
@@ -1475,6 +1476,7 @@
|
||||
"cloudtrail_threat_detection_enumeration",
|
||||
"cloudtrail_threat_detection_privilege_escalation",
|
||||
"cloudtrail_threat_detection_llm_jacking",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"cloudtrail_multi_region_enabled_logging_management_events"
|
||||
]
|
||||
@@ -1571,6 +1573,7 @@
|
||||
"cloudtrail_threat_detection_llm_jacking",
|
||||
"cloudtrail_threat_detection_enumeration",
|
||||
"cloudtrail_multi_region_enabled_logging_management_events",
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"cloudwatch_log_metric_filter_unauthorized_api_calls",
|
||||
"cloudwatch_log_metric_filter_authentication_failures",
|
||||
|
||||
@@ -818,6 +818,7 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_multi_region_enabled_logging_management_events",
|
||||
"cloudtrail_s3_dataevents_read_enabled",
|
||||
|
||||
@@ -346,6 +346,7 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"cloudtrail_bedrock_logging_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"cloudwatch_changes_to_network_acls_alarm_configured",
|
||||
"cloudwatch_changes_to_network_gateways_alarm_configured",
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudtrail_bedrock_logging_enabled",
|
||||
"CheckTitle": "CloudTrail logs Amazon Bedrock API calls for security auditing",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "cloudtrail",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"ResourceGroup": "monitoring",
|
||||
"Description": "**At least one actively logging CloudTrail trail** records **Amazon Bedrock API activity** through management events or advanced event selectors targeting Bedrock resources.\n\nThis check covers **control-plane** operations such as configuration changes through CloudTrail management events and can also cover **data-plane** Bedrock events when advanced event selectors target Bedrock resource types.",
|
||||
"Risk": "Without CloudTrail logging for Bedrock control-plane operations, changes to prompts, guardrails, agents, flows, or knowledge bases can become invisible, weakening forensics and incident response. Management events do not capture `InvokeModel`; pair this control with `bedrock_model_invocation_logging_enabled` or Bedrock data event selectors for invocation visibility.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/bedrock/latest/userguide/logging-using-cloudtrail.html",
|
||||
"https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws cloudtrail put-event-selectors --trail-name <example_resource_name> --advanced-event-selectors '[{\"Name\":\"Bedrock data events\",\"FieldSelectors\":[{\"Field\":\"eventCategory\",\"Equals\":[\"Data\"]},{\"Field\":\"resources.type\",\"Equals\":[\"AWS::Bedrock::Model\",\"AWS::Bedrock::Guardrail\",\"AWS::Bedrock::AgentAlias\",\"AWS::Bedrock::FlowAlias\",\"AWS::Bedrock::InlineAgent\",\"AWS::Bedrock::KnowledgeBase\",\"AWS::Bedrock::Prompt\"]}]}]'",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: enable Bedrock data event logging on an actively logging trail\nResources:\n ExampleTrail:\n Type: AWS::CloudTrail::Trail\n Properties:\n TrailName: <example_resource_name>\n S3BucketName: <example_resource_name>\n IsLogging: true\n AdvancedEventSelectors:\n - Name: Bedrock data events\n FieldSelectors:\n - Field: eventCategory\n Equals:\n - Data\n - Field: resources.type # CRITICAL: target Bedrock resources\n Equals:\n - AWS::Bedrock::Model\n - AWS::Bedrock::Guardrail\n - AWS::Bedrock::AgentAlias\n - AWS::Bedrock::FlowAlias\n - AWS::Bedrock::InlineAgent\n - AWS::Bedrock::KnowledgeBase\n - AWS::Bedrock::Prompt\n```",
|
||||
"Other": "1. In the AWS Console, open CloudTrail and select a trail that is actively logging\n2. Edit the trail and enable Management events to capture Bedrock control-plane operations, or add Bedrock advanced data event selectors for data-plane visibility\n3. If using data events, select the Bedrock resource types you want to log\n4. Save changes and confirm the trail remains in logging state",
|
||||
"Terraform": "```hcl\n# Terraform: enable Bedrock data event logging on an actively logging trail\nresource \"aws_cloudtrail\" \"example_resource\" {\n name = \"example_resource\"\n s3_bucket_name = \"example_resource\"\n\n advanced_event_selector {\n name = \"Bedrock data events\"\n field_selector {\n field = \"eventCategory\"\n equals = [\"Data\"]\n }\n field_selector {\n field = \"resources.type\" # CRITICAL: target Bedrock resources\n equals = [\"AWS::Bedrock::Model\", \"AWS::Bedrock::Guardrail\", \"AWS::Bedrock::AgentAlias\", \"AWS::Bedrock::FlowAlias\", \"AWS::Bedrock::InlineAgent\", \"AWS::Bedrock::KnowledgeBase\", \"AWS::Bedrock::Prompt\"]\n }\n }\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable CloudTrail logging for Amazon Bedrock on **at least one actively logging trail**. At minimum, enable **management events** to capture Bedrock control-plane operations. For invocation-level and other data-plane visibility, add **advanced event selectors** targeting Bedrock resource types or pair this control with `bedrock_model_invocation_logging_enabled`.\n\nFor broader region coverage, pair this control with a separate multi-region CloudTrail check. Centralize logs in an encrypted bucket or CloudWatch Logs to support **defense in depth** and forensic readiness for AI workloads.",
|
||||
"Url": "https://hub.prowler.com/check/cloudtrail_bedrock_logging_enabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"logging",
|
||||
"forensics-ready",
|
||||
"gen-ai"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"cloudtrail_multi_region_enabled_logging_management_events",
|
||||
"bedrock_model_invocation_logging_enabled"
|
||||
],
|
||||
"Notes": "This check passes when CloudTrail captures Bedrock control-plane activity via management events or Bedrock data events via advanced selectors. It does not require multi-region coverage, and it does not by itself guarantee `InvokeModel` visibility unless Bedrock data events are selected; use `bedrock_model_invocation_logging_enabled` for model invocation logs. Additional advanced selector filters such as `eventName` or `resources.ARN` can further narrow effective coverage and should be reviewed explicitly."
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
cloudtrail_client,
|
||||
)
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Event_Selector,
|
||||
)
|
||||
|
||||
|
||||
class cloudtrail_bedrock_logging_enabled(Check):
|
||||
"""Ensure CloudTrail is configured to log Amazon Bedrock API calls.
|
||||
|
||||
This check verifies whether at least one CloudTrail trail is configured to
|
||||
capture Amazon Bedrock control-plane API calls through management events or
|
||||
Bedrock data events through advanced event selectors.
|
||||
|
||||
- PASS: A trail logs Bedrock control-plane API calls via management events
|
||||
or Bedrock data events via Bedrock-specific advanced event selectors.
|
||||
- FAIL: No CloudTrail trail is configured to log Bedrock API calls.
|
||||
"""
|
||||
|
||||
# Bedrock resource types supported by CloudTrail advanced event selectors.
|
||||
BEDROCK_RESOURCE_TYPES = frozenset(
|
||||
{
|
||||
"AWS::Bedrock::AgentAlias",
|
||||
"AWS::Bedrock::FlowAlias",
|
||||
"AWS::Bedrock::Guardrail",
|
||||
"AWS::Bedrock::InlineAgent",
|
||||
"AWS::Bedrock::KnowledgeBase",
|
||||
"AWS::Bedrock::Model",
|
||||
"AWS::Bedrock::Prompt",
|
||||
}
|
||||
)
|
||||
# Bedrock control-plane event sources, including Bedrock Data Automation.
|
||||
BEDROCK_EVENT_SOURCES = frozenset(
|
||||
{
|
||||
"bedrock.amazonaws.com",
|
||||
"bedrock-agent.amazonaws.com",
|
||||
"bedrock-runtime.amazonaws.com",
|
||||
"bedrock-agent-runtime.amazonaws.com",
|
||||
"bedrock-data-automation.amazonaws.com",
|
||||
"bedrock-data-automation-runtime.amazonaws.com",
|
||||
}
|
||||
)
|
||||
|
||||
def execute(self) -> list[Check_Report_AWS]:
|
||||
"""Execute the check logic.
|
||||
|
||||
Returns:
|
||||
A list of reports containing the result of the check.
|
||||
"""
|
||||
findings = []
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_logging:
|
||||
for data_event in trail.data_events:
|
||||
match_type = self._get_bedrock_match_type(data_event)
|
||||
if match_type:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource=trail
|
||||
)
|
||||
report.region = trail.home_region
|
||||
report.status = "PASS"
|
||||
if match_type == "classic_management":
|
||||
report.status_extended = (
|
||||
f"Trail {trail.name} from home region "
|
||||
f"{trail.home_region} has management events "
|
||||
"enabled to log Amazon Bedrock control-plane "
|
||||
"API calls."
|
||||
)
|
||||
elif match_type == "advanced_management":
|
||||
report.status_extended = (
|
||||
f"Trail {trail.name} from home region "
|
||||
f"{trail.home_region} has an advanced "
|
||||
"management event selector to log Amazon "
|
||||
"Bedrock control-plane API calls."
|
||||
)
|
||||
else:
|
||||
report.status_extended = (
|
||||
f"Trail {trail.name} from home region "
|
||||
f"{trail.home_region} has an advanced data "
|
||||
"event selector to log Amazon Bedrock API "
|
||||
"calls."
|
||||
)
|
||||
findings.append(report)
|
||||
break
|
||||
if not findings:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudTrail trails are configured to log Amazon Bedrock API calls."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
def _get_bedrock_match_type(self, data_event: Event_Selector) -> str | None:
|
||||
"""Return the Bedrock logging match type for an event selector.
|
||||
|
||||
Args:
|
||||
data_event: An Event_Selector object from the trail.
|
||||
|
||||
Returns:
|
||||
The matching selector type, or None if the selector does not log
|
||||
the Bedrock events covered by this check.
|
||||
"""
|
||||
if not data_event.is_advanced:
|
||||
if self._logs_classic_management_events(data_event.event_selector):
|
||||
return "classic_management"
|
||||
return None
|
||||
|
||||
field_selectors = data_event.event_selector.get("FieldSelectors", [])
|
||||
if self._logs_advanced_management_events(field_selectors):
|
||||
return "advanced_management"
|
||||
if self._logs_advanced_bedrock_data_events(field_selectors):
|
||||
return "advanced_data"
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _logs_classic_management_events(event_selector: dict) -> bool:
|
||||
"""Check whether a classic selector logs Bedrock control-plane events."""
|
||||
return event_selector.get(
|
||||
"IncludeManagementEvents", True
|
||||
) and event_selector.get("ReadWriteType", "All") in ("All", "WriteOnly")
|
||||
|
||||
def _logs_advanced_management_events(self, field_selectors: list[dict]) -> bool:
|
||||
"""Check whether advanced selectors log Bedrock control-plane events."""
|
||||
event_category_selectors = [
|
||||
field for field in field_selectors if field.get("Field") == "eventCategory"
|
||||
]
|
||||
if not self._selectors_match_value("Management", event_category_selectors):
|
||||
return False
|
||||
|
||||
read_only_selectors = [
|
||||
field for field in field_selectors if field.get("Field") == "readOnly"
|
||||
]
|
||||
has_read_only_restriction = bool(read_only_selectors) and not any(
|
||||
self._field_selector_matches_value("false", selector)
|
||||
for selector in read_only_selectors
|
||||
)
|
||||
|
||||
return not has_read_only_restriction and self._logs_bedrock_management_events(
|
||||
field_selectors
|
||||
)
|
||||
|
||||
def _logs_advanced_bedrock_data_events(self, field_selectors: list[dict]) -> bool:
|
||||
"""Check whether advanced selectors log Bedrock data events."""
|
||||
event_category_selectors = [
|
||||
field for field in field_selectors if field.get("Field") == "eventCategory"
|
||||
]
|
||||
if not self._selectors_match_value("Data", event_category_selectors):
|
||||
return False
|
||||
|
||||
resource_type_selectors = [
|
||||
field for field in field_selectors if field.get("Field") == "resources.type"
|
||||
]
|
||||
return any(
|
||||
self._selectors_match_value(resource_type, resource_type_selectors)
|
||||
for resource_type in self.BEDROCK_RESOURCE_TYPES
|
||||
)
|
||||
|
||||
def _logs_bedrock_management_events(self, field_selectors: list[dict]) -> bool:
|
||||
"""Check whether advanced management selectors include Bedrock sources."""
|
||||
event_source_selectors = [
|
||||
field for field in field_selectors if field.get("Field") == "eventSource"
|
||||
]
|
||||
if not event_source_selectors:
|
||||
return True
|
||||
|
||||
return any(
|
||||
self._selectors_match_value(event_source, event_source_selectors)
|
||||
for event_source in self.BEDROCK_EVENT_SOURCES
|
||||
)
|
||||
|
||||
def _selectors_match_value(self, value: str, selectors: list[dict]) -> bool:
|
||||
"""Check whether a candidate value satisfies all selectors for a field."""
|
||||
return bool(selectors) and all(
|
||||
self._field_selector_matches_value(value, selector)
|
||||
for selector in selectors
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _field_selector_matches_value(value: str, selector: dict) -> bool:
|
||||
"""Evaluate a CloudTrail advanced field selector against a candidate value."""
|
||||
conditions = []
|
||||
|
||||
if "Equals" in selector:
|
||||
conditions.append(value in selector["Equals"])
|
||||
if "NotEquals" in selector:
|
||||
conditions.append(value not in selector["NotEquals"])
|
||||
if "StartsWith" in selector:
|
||||
conditions.append(
|
||||
any(value.startswith(prefix) for prefix in selector["StartsWith"])
|
||||
)
|
||||
if "NotStartsWith" in selector:
|
||||
conditions.append(
|
||||
all(
|
||||
not value.startswith(prefix) for prefix in selector["NotStartsWith"]
|
||||
)
|
||||
)
|
||||
if "EndsWith" in selector:
|
||||
conditions.append(
|
||||
any(value.endswith(suffix) for suffix in selector["EndsWith"])
|
||||
)
|
||||
if "NotEndsWith" in selector:
|
||||
conditions.append(
|
||||
all(not value.endswith(suffix) for suffix in selector["NotEndsWith"])
|
||||
)
|
||||
|
||||
return all(conditions) if conditions else True
|
||||
+1020
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user