Skip to content

PR Scan Policy Reference

PR scan policies define pass/fail gates for pull request scans. When a PR is scanned, these policies determine whether the PR passes the security check, and what feedback is posted as a comment or check run.


Package

package mayo.pr_scan

Required output

Variable Type Description
result string "pass" or "fail"

Optional outputs:

Variable Type Description
message string Message posted as a PR comment
block boolean If true, mark the GitHub check run as failed (default follows result)
findings_to_annotate array Finding IDs to annotate inline on the PR diff

Input schema

{
  "pull_request": {
    "number": "integer — PR number",
    "title": "string — PR title",
    "author": "string — GitHub username",
    "base_branch": "string — target branch (e.g., main)",
    "head_branch": "string — source branch",
    "labels": ["string — PR labels"],
    "draft": "boolean — true if PR is a draft"
  },
  "scan_results": {
    "new_findings": [
      {
        "id": "string",
        "title": "string",
        "severity": "string",
        "scanner": "string",
        "rule_id": "string",
        "file_path": "string",
        "line_number": "integer",
        "cve_id": "string",
        "package": "string"
      }
    ],
    "fixed_findings": [
      { "...": "same structure as new_findings" }
    ],
    "total_new": "integer",
    "total_fixed": "integer",
    "by_severity": {
      "critical": "integer",
      "high": "integer",
      "medium": "integer",
      "low": "integer",
      "info": "integer"
    }
  },
  "asset": {
    "name": "string",
    "full_name": "string"
  },
  "organization": {
    "id": "string",
    "name": "string"
  }
}

Examples

Block on any critical finding

package mayo.pr_scan

import rego.v1

result := "fail" if {
    input.scan_results.by_severity.critical > 0
}

default result := "pass"

message := sprintf("Blocked: %d critical finding(s) introduced", [
    input.scan_results.by_severity.critical
]) if {
    result == "fail"
}

Block on critical or high findings

package mayo.pr_scan

import rego.v1

result := "fail" if {
    count(blocking_findings) > 0
}

default result := "pass"

blocking_findings := [f |
    some f in input.scan_results.new_findings
    f.severity in ["critical", "high"]
]

message := sprintf("%d blocking finding(s): %s", [
    count(blocking_findings),
    concat(", ", [f.title | some f in blocking_findings])
]) if {
    count(blocking_findings) > 0
}

Warn but don't block

package mayo.pr_scan

import rego.v1

default result := "pass"
default block := false

message := sprintf("Warning: %d new finding(s) detected (%d critical, %d high)", [
    input.scan_results.total_new,
    input.scan_results.by_severity.critical,
    input.scan_results.by_severity.high
]) if {
    input.scan_results.total_new > 0
}

Skip draft PRs

package mayo.pr_scan

import rego.v1

# Always pass draft PRs
result := "pass" if {
    input.pull_request.draft == true
}

# Block non-draft PRs with critical findings
result := "fail" if {
    not input.pull_request.draft
    input.scan_results.by_severity.critical > 0
}

default result := "pass"

Different rules per branch

package mayo.pr_scan

import rego.v1

# Strict rules for PRs targeting main
result := "fail" if {
    input.pull_request.base_branch == "main"
    input.scan_results.total_new > 0
    input.scan_results.by_severity.critical + input.scan_results.by_severity.high > 0
}

# Lenient rules for PRs targeting develop
result := "fail" if {
    input.pull_request.base_branch == "develop"
    input.scan_results.by_severity.critical > 0
}

default result := "pass"

How PR scanning works

  1. A pull request is opened or updated on a connected GitHub repository.
  2. Mayo ASPM receives the webhook event and triggers a scan of the PR diff.
  3. The scan compares the head branch against the base branch.
  4. New findings are those present in the head but not in the base.
  5. Fixed findings are those present in the base but not in the head.
  6. PR scan policies evaluate the results.
  7. A GitHub check run is created (pass/fail) and an optional comment is posted.

Differential scanning

PR scans only report findings introduced by the PR, not all findings in the repository. This prevents developers from being blocked by pre-existing issues.


Check run and comment behavior

result block GitHub check run PR comment
"pass" (ignored) Success Posted if message is set
"fail" true (default) Failure (blocks merge) Posted with message
"fail" false Success (warning only) Posted with message

Inline annotations

Set findings_to_annotate to highlight specific findings on the PR diff:

findings_to_annotate := [f.id |
    some f in input.scan_results.new_findings
    f.severity in ["critical", "high"]
]

This adds inline comments on the exact lines where findings were detected.


Next steps