Skip to content

Triage Policy Reference

Triage policies evaluate incoming findings and assign a triage decision: accept, reject, or defer. They are the first line of defense against alert fatigue.


Package

package mayo.triage

Required output

Variable Type Values Description
decision string "accept", "reject", "defer" The triage decision

Optional outputs:

Variable Type Description
reason string Human-readable explanation shown in the finding timeline
tags array of strings Tags to add to the finding

Input schema

{
  "finding": {
    "id": "string — unique finding identifier",
    "title": "string — human-readable title",
    "description": "string — detailed description",
    "severity": "string — critical|high|medium|low|info",
    "scanner": "string — scanner name (semgrep, grype, trivy, gitleaks, etc.)",
    "scanner_type": "string — sast|sca|secret|container|iac",
    "rule_id": "string — scanner-specific rule identifier",
    "file_path": "string — relative file path within the asset",
    "line_number": "integer — line number (0 if not applicable)",
    "cve_id": "string — CVE identifier (empty if none)",
    "cwe_id": "string — CWE identifier (empty if none)",
    "package": "string — affected package name (SCA only)",
    "package_version": "string — installed version",
    "fixed_version": "string — version that fixes the issue (empty if unknown)",
    "age_days": "integer — days since first detected",
    "epss_score": "float — EPSS probability (0.0-1.0)",
    "in_kev": "boolean — true if in CISA KEV catalog",
    "asset": {
      "name": "string — asset short name",
      "source": "string — github|upload|api",
      "full_name": "string — e.g., org/repo"
    },
    "project": {
      "id": "string — project identifier",
      "name": "string — project name"
    }
  },
  "organization": {
    "id": "string",
    "name": "string"
  }
}

Examples

Basic severity routing

package mayo.triage

import rego.v1

default decision := "defer"

decision := "accept" if {
    input.finding.severity in ["critical", "high"]
}

decision := "reject" if {
    input.finding.severity == "info"
}

Suppress test and vendor files

package mayo.triage

import rego.v1

decision := "reject" if {
    some pattern in ["/test/", "/spec/", "/__tests__/", "/vendor/", "/node_modules/"]
    contains(input.finding.file_path, pattern)
}

reason := "Finding is in a non-production path" if {
    decision == "reject"
}

Accept known-exploited vulnerabilities

package mayo.triage

import rego.v1

decision := "accept" if {
    input.finding.in_kev == true
}

reason := "CVE is in the CISA Known Exploited Vulnerabilities catalog" if {
    input.finding.in_kev == true
}

Scanner-specific suppression

package mayo.triage

import rego.v1

# Suppress noisy Semgrep rules
noisy_rules := {
    "generic.secrets.gitleaks.generic-api-key",
    "python.lang.best-practice.open-never-closed",
    "javascript.express.best-practice.helmet"
}

decision := "reject" if {
    input.finding.scanner == "semgrep"
    input.finding.rule_id in noisy_rules
}

reason := concat("", ["Suppressed noisy rule: ", input.finding.rule_id]) if {
    decision == "reject"
}

EPSS-based triage

package mayo.triage

import rego.v1

decision := "accept" if {
    input.finding.epss_score > 0.5
}

decision := "reject" if {
    input.finding.severity == "low"
    input.finding.epss_score < 0.01
    input.finding.age_days > 180
}

Evaluation behavior

  • Triage policies run once per finding when the finding is first ingested.
  • If the finding is re-detected in a subsequent scan, triage is not re-evaluated unless the finding's attributes change (e.g., severity updated by the scanner).
  • Multiple triage policies can exist. If multiple policies produce different decisions for the same finding, the most specific scoped policy wins. See Policy scoping.

Testing tips

  1. Load a real finding from your scan data into the playground.
  2. Verify that the decision matches your expectations.
  3. Test edge cases: empty cve_id, missing file_path, zero age_days.
  4. Save test cases for regression testing.

Next steps