From f7b009de9b2d7cab105d73eac0f65ffa07840e36 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Sat, 17 Jan 2026 21:20:32 -0300 Subject: [PATCH] Add type checking in Rego policies Signed-off-by: Arthur Silva Sens --- build/semconv/policies/prometheus.rego | 145 +++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/build/semconv/policies/prometheus.rego b/build/semconv/policies/prometheus.rego index 24ec22bf3a..41eb3b9d8b 100644 --- a/build/semconv/policies/prometheus.rego +++ b/build/semconv/policies/prometheus.rego @@ -123,6 +123,151 @@ deny contains metric_violation( not group.annotations.prometheus.objectives } +# ============================================================================= +# Type Validation Rules +# ============================================================================= + +# Rule 8: buckets must be an array of numbers +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.buckets must be an array, got %v", [group.metric_name, type_name(group.annotations.prometheus.buckets)]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + group.annotations.prometheus.buckets + not is_array(group.annotations.prometheus.buckets) +} + +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.buckets must contain only numbers, found non-number at index %d", [group.metric_name, idx]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + is_array(group.annotations.prometheus.buckets) + bucket := group.annotations.prometheus.buckets[idx] + not is_number(bucket) +} + +# Rule 10: exponential_buckets must be an object with start, factor, count +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.exponential_buckets must be an object, got %v", [group.metric_name, type_name(group.annotations.prometheus.exponential_buckets)]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + group.annotations.prometheus.exponential_buckets + not is_object(group.annotations.prometheus.exponential_buckets) +} + +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.exponential_buckets.start is required and must be a number", [group.metric_name]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + is_object(group.annotations.prometheus.exponential_buckets) + not is_number(group.annotations.prometheus.exponential_buckets.start) +} + +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.exponential_buckets.factor is required and must be a number", [group.metric_name]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + is_object(group.annotations.prometheus.exponential_buckets) + not is_number(group.annotations.prometheus.exponential_buckets.factor) +} + +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.exponential_buckets.count is required and must be a number", [group.metric_name]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + is_object(group.annotations.prometheus.exponential_buckets) + not is_number(group.annotations.prometheus.exponential_buckets.count) +} + +# Rule 11: bucket_factor must be a number +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.bucket_factor must be a number, got %v", [group.metric_name, type_name(group.annotations.prometheus.bucket_factor)]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + group.annotations.prometheus.bucket_factor + not is_number(group.annotations.prometheus.bucket_factor) +} + +# Rule 12: max_bucket_number must be a number +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.max_bucket_number must be a number, got %v", [group.metric_name, type_name(group.annotations.prometheus.max_bucket_number)]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + group.annotations.prometheus.max_bucket_number + not is_number(group.annotations.prometheus.max_bucket_number) +} + +# Rule 13: min_reset_duration must be a string +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.min_reset_duration must be a string (e.g., \"1h\", \"30m\"), got %v", [group.metric_name, type_name(group.annotations.prometheus.min_reset_duration)]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + group.annotations.prometheus.min_reset_duration + not is_string(group.annotations.prometheus.min_reset_duration) +} + +# Rule 14: objectives must be an object (map) +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.objectives must be an object (map of quantile to error), got %v", [group.metric_name, type_name(group.annotations.prometheus.objectives)]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + group.annotations.prometheus.objectives + not is_object(group.annotations.prometheus.objectives) +} + +# Rule 15: objectives values must be numbers +deny contains metric_violation( + sprintf("Metric '%s': annotations.prometheus.objectives values must be numbers, found non-number for key '%s'", [group.metric_name, key]), + group.id, + group.metric_name +) if { + group := input.groups[_] + group.type == "metric" + group.instrument == "histogram" + is_object(group.annotations.prometheus.objectives) + val := group.annotations.prometheus.objectives[key] + not is_number(val) +} + # ============================================================================= # Helper Functions # =============================================================================