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¶
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¶
- A pull request is opened or updated on a connected GitHub repository.
- Mayo ASPM receives the webhook event and triggers a scan of the PR diff.
- The scan compares the head branch against the base branch.
- New findings are those present in the head but not in the base.
- Fixed findings are those present in the base but not in the head.
- PR scan policies evaluate the results.
- 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¶
- PR scanning strategy — best practices
- GitHub integration — webhook and permissions setup
- PR scanning troubleshooting — common issues