Skip to content

Priority Policy Reference

Priority policies assign a numeric priority score to findings after triage. This score determines the order in which findings appear in dashboards and which findings are ticketed first.


Package

package mayo.priority

Required output

Variable Type Range Description
priority integer 0-100 Priority score (higher = more urgent)

Optional outputs:

Variable Type Description
label string Human-readable label (e.g., "P1", "Critical")
reason string Explanation of the score

Input schema

The input includes all fields from the triage input, plus:

{
  "finding": { "...same as triage input..." },
  "triage_decision": "accept",
  "organization": { "id": "string", "name": "string" }
}

Info

Priority policies only evaluate findings that were accepted during triage. Rejected and deferred findings are not scored.


Scoring strategy

A common approach is to start with a base score from severity, then adjust with modifiers:

Base score (severity)
  + EPSS modifier
  + Age modifier
  + Fix availability modifier
  + KEV modifier
  ─────────────────
  = Final priority (capped at 100)

Examples

Simple severity-based scoring

package mayo.priority

import rego.v1

priority := 100 if input.finding.severity == "critical"
priority := 75 if input.finding.severity == "high"
priority := 50 if input.finding.severity == "medium"
priority := 25 if input.finding.severity == "low"

Weighted scoring with modifiers

package mayo.priority

import rego.v1

# Base score from severity
base := 80 if input.finding.severity == "critical"
base := 60 if input.finding.severity == "high"
base := 40 if input.finding.severity == "medium"
base := 20 if input.finding.severity == "low"

# EPSS modifier: +0 to +15 based on exploit probability
epss_mod := round(input.finding.epss_score * 15)

# KEV modifier: +10 if in CISA KEV
kev_mod := 10 if input.finding.in_kev == true
default kev_mod := 0

# Fix available: +5 if a fix exists
fix_mod := 5 if input.finding.fixed_version != ""
default fix_mod := 0

# Age penalty: +1 per 30 days, max +10
age_mod := min([round(input.finding.age_days / 30), 10])

# Final score capped at 100
raw := base + epss_mod + kev_mod + fix_mod + age_mod
priority := min([raw, 100])

# Human-readable label
label := "P1 — Critical" if priority >= 80
label := "P2 — High" if { priority >= 60; priority < 80 }
label := "P3 — Medium" if { priority >= 40; priority < 60 }
label := "P4 — Low" if priority < 40

Context-aware scoring

package mayo.priority

import rego.v1

# Production-facing assets get higher priority
base := 90 if {
    input.finding.severity == "critical"
    input.finding.asset.name in ["api-gateway", "auth-service", "payment-service"]
}

base := 70 if {
    input.finding.severity == "critical"
}

default base := 50

priority := base

Priority labels

If you define a label output, it appears alongside the numeric score in the UI:

Score range Suggested label Typical SLA
80-100 P1 — Critical 24 hours
60-79 P2 — High 7 days
40-59 P3 — Medium 30 days
0-39 P4 — Low 90 days

Evaluation behavior

  • Priority policies run after triage on accepted findings only.
  • If multiple priority policies exist, the most specific scoped policy wins.
  • Priority scores can be recalculated by triggering a re-evaluation from the findings view.

Testing tips

  1. Test with findings of each severity to verify your score distribution.
  2. Check that edge cases (EPSS = 0, no CVE, age = 0) produce reasonable scores.
  3. Verify that your labels align with your organization's SLA expectations.

Next steps