193 lines
4.8 KiB
Rego
193 lines
4.8 KiB
Rego
# -----------------------------------------------------------------------------
|
|
# release-aggregate.rego
|
|
# Sprint: SPRINT_20260118_027_Policy_cve_release_gates
|
|
# Task: TASK-027-08 - OPA/Rego Policy Examples
|
|
# Description: Aggregate CVE count limits per release
|
|
# -----------------------------------------------------------------------------
|
|
|
|
package stellaops.gates.aggregate
|
|
|
|
import future.keywords.if
|
|
import future.keywords.in
|
|
|
|
# Default allow if all counts within limits
|
|
default allow = true
|
|
|
|
# Block if any severity count exceeds limit
|
|
allow = false if {
|
|
counts.critical > max_critical
|
|
}
|
|
|
|
allow = false if {
|
|
counts.high > max_high
|
|
}
|
|
|
|
allow = false if {
|
|
counts.medium > max_medium
|
|
}
|
|
|
|
allow = false if {
|
|
max_low != null
|
|
counts.low > max_low
|
|
}
|
|
|
|
allow = false if {
|
|
max_total != null
|
|
counts.total > max_total
|
|
}
|
|
|
|
# Get CVEs to count (optionally filtered)
|
|
counted_cves := [cve |
|
|
some cve in input.cve_findings
|
|
should_count(cve)
|
|
]
|
|
|
|
# Determine if CVE should be counted
|
|
should_count(cve) if {
|
|
not config_only_reachable
|
|
not config_exclude_suppressed
|
|
}
|
|
|
|
should_count(cve) if {
|
|
config_only_reachable
|
|
cve.is_reachable == true
|
|
not config_exclude_suppressed
|
|
}
|
|
|
|
should_count(cve) if {
|
|
not config_only_reachable
|
|
config_exclude_suppressed
|
|
not cve.is_suppressed
|
|
}
|
|
|
|
should_count(cve) if {
|
|
config_only_reachable
|
|
cve.is_reachable == true
|
|
config_exclude_suppressed
|
|
not cve.is_suppressed
|
|
}
|
|
|
|
# Classify severity from CVSS score
|
|
severity(cve) := "critical" if {
|
|
cve.cvss_score >= 9.0
|
|
}
|
|
|
|
severity(cve) := "high" if {
|
|
cve.cvss_score >= 7.0
|
|
cve.cvss_score < 9.0
|
|
}
|
|
|
|
severity(cve) := "medium" if {
|
|
cve.cvss_score >= 4.0
|
|
cve.cvss_score < 7.0
|
|
}
|
|
|
|
severity(cve) := "low" if {
|
|
cve.cvss_score >= 0.1
|
|
cve.cvss_score < 4.0
|
|
}
|
|
|
|
severity(cve) := "unknown" if {
|
|
not cve.cvss_score
|
|
}
|
|
|
|
# Count CVEs by severity
|
|
counts := {
|
|
"critical": count([c | some c in counted_cves; severity(c) == "critical"]),
|
|
"high": count([c | some c in counted_cves; severity(c) == "high"]),
|
|
"medium": count([c | some c in counted_cves; severity(c) == "medium"]),
|
|
"low": count([c | some c in counted_cves; severity(c) == "low"]),
|
|
"unknown": count([c | some c in counted_cves; severity(c) == "unknown"]),
|
|
"total": count(counted_cves),
|
|
}
|
|
|
|
# Get limits with environment override support
|
|
max_critical := env_config.max_critical if {
|
|
env_config := input.config.environments[input.environment]
|
|
env_config.max_critical != null
|
|
} else := input.config.max_critical if {
|
|
input.config.max_critical != null
|
|
} else := 0 # Default: no critical allowed
|
|
|
|
max_high := env_config.max_high if {
|
|
env_config := input.config.environments[input.environment]
|
|
env_config.max_high != null
|
|
} else := input.config.max_high if {
|
|
input.config.max_high != null
|
|
} else := 3 # Default: max 3 high
|
|
|
|
max_medium := env_config.max_medium if {
|
|
env_config := input.config.environments[input.environment]
|
|
env_config.max_medium != null
|
|
} else := input.config.max_medium if {
|
|
input.config.max_medium != null
|
|
} else := 20 # Default: max 20 medium
|
|
|
|
max_low := env_config.max_low if {
|
|
env_config := input.config.environments[input.environment]
|
|
env_config.max_low != null
|
|
} else := input.config.max_low if {
|
|
input.config.max_low != null
|
|
} else := null # Default: unlimited
|
|
|
|
max_total := env_config.max_total if {
|
|
env_config := input.config.environments[input.environment]
|
|
env_config.max_total != null
|
|
} else := input.config.max_total if {
|
|
input.config.max_total != null
|
|
} else := null # Default: unlimited
|
|
|
|
# Configuration flags
|
|
config_only_reachable if {
|
|
input.config.only_reachable == true
|
|
}
|
|
|
|
config_exclude_suppressed if {
|
|
input.config.count_suppressed == false
|
|
}
|
|
|
|
config_exclude_suppressed if {
|
|
not input.config.count_suppressed
|
|
}
|
|
|
|
# Denial messages
|
|
deny[msg] if {
|
|
counts.critical > max_critical
|
|
msg := sprintf("Critical CVE count exceeds limit: %d > %d", [counts.critical, max_critical])
|
|
}
|
|
|
|
deny[msg] if {
|
|
counts.high > max_high
|
|
msg := sprintf("High CVE count exceeds limit: %d > %d", [counts.high, max_high])
|
|
}
|
|
|
|
deny[msg] if {
|
|
counts.medium > max_medium
|
|
msg := sprintf("Medium CVE count exceeds limit: %d > %d", [counts.medium, max_medium])
|
|
}
|
|
|
|
deny[msg] if {
|
|
max_low != null
|
|
counts.low > max_low
|
|
msg := sprintf("Low CVE count exceeds limit: %d > %d", [counts.low, max_low])
|
|
}
|
|
|
|
deny[msg] if {
|
|
max_total != null
|
|
counts.total > max_total
|
|
msg := sprintf("Total CVE count exceeds limit: %d > %d", [counts.total, max_total])
|
|
}
|
|
|
|
# Summary for reporting
|
|
summary := {
|
|
"counts": counts,
|
|
"limits": {
|
|
"max_critical": max_critical,
|
|
"max_high": max_high,
|
|
"max_medium": max_medium,
|
|
"max_low": max_low,
|
|
"max_total": max_total,
|
|
},
|
|
"environment": input.environment,
|
|
}
|