Security Advisory
MidnightBSD/src
Handle a security fix end to end for MidnightBSD src - triage a FreeBSD security advisory (FreeBSD-SA-) or CVE against this tree, port the fix to master and both stable branches, add the UPDATING…
A skill your agent uses when opening or updating a GitHub pull request after Phase 5 of /compliance:analyze-cve has applied and verified a CVE fix locally.
$ npx skills add openshift-eng/ai-helpers --skill create-fix-pr -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install openshift-eng/ai-helpers create-fix-pr --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/compliance/skills/create-fix-pr .claude/skills/create-fix-pr && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "create-fix-pr" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/compliance/skills/create-fix-pr into .claude/skills/create-fix-pr/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-fix-pr", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/openshift-eng/ai-helpers/tree/main/plugins/compliance/skills/create-fix-prType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add openshift-eng/ai-helpers --skill create-fix-pr -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install openshift-eng/ai-helpers create-fix-pr --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/compliance/skills/create-fix-pr .agents/skills/create-fix-pr && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "create-fix-pr" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/compliance/skills/create-fix-pr into .agents/skills/create-fix-pr/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-fix-pr", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add openshift-eng/ai-helpers --skill create-fix-pr -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install openshift-eng/ai-helpers create-fix-pr --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/compliance/skills/create-fix-pr .cursor/skills/create-fix-pr && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "create-fix-pr" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/compliance/skills/create-fix-pr into .cursor/skills/create-fix-pr/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-fix-pr", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/openshift-eng/ai-helpers.git --path plugins/compliance/skills/create-fix-pr--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add openshift-eng/ai-helpers --skill create-fix-pr -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install openshift-eng/ai-helpers create-fix-pr --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/compliance/skills/create-fix-pr .gemini/skills/create-fix-pr && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "create-fix-pr" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/compliance/skills/create-fix-pr into .gemini/skills/create-fix-pr/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-fix-pr", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install openshift-eng/ai-helpers create-fix-prInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add openshift-eng/ai-helpers --skill create-fix-pr -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/compliance/skills/create-fix-pr .github/skills/create-fix-pr && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "create-fix-pr" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/compliance/skills/create-fix-pr into .github/skills/create-fix-pr/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-fix-pr", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add openshift-eng/ai-helpers --skill create-fix-pr -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install openshift-eng/ai-helpers create-fix-pr --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/compliance/skills/create-fix-pr .opencode/skills/create-fix-pr && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "create-fix-pr" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/compliance/skills/create-fix-pr into .opencode/skills/create-fix-pr/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-fix-pr", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
create-fix-prA skill your agent uses when opening or updating a GitHub pull request after Phase 5 of /compliance:analyze-cve has applied and verified a CVE fix locally.
Create Fix PR is an agent skill from openshift-eng/ai-helpers. Use when opening or updating a GitHub pull request after Phase 5 of /compliance:analyze-cve has applied and verified a CVE fix locally.
Its SKILL.md is about 6.2k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Security, covering Vulnerability scanning. It works with GitHub and Jira. The repository describes itself as: Developer productivity tools for Claude Code & other AI assistants. The licence is Apache-2.0.
Read from SKILL.md and the folder at commit a627176. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
gitghjqgomakeFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
github.comcli.github.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Create Fix PR loads about 6.2k tokens when it runs. Until then it costs about 38 tokens; SKILL.md has 2,601 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from openshift-eng/ai-helpers at commit a627176, republished under its Apache-2.0 licence (© openshift-eng). 2,601 words, ~6,244 tokens.
.claude/skills/create-fix-pr/SKILL.md (or your agent's skills folder).Opens a GitHub pull request for the CVE fix already applied in REPO_DIR (Phase 5). Never commits, pushes, or opens a PR without explicit approval — interactive, or AUTO_APPROVE=yes recorded once upfront (see Autonomous Mode). Direct CVE mode (no --jira=/--jql=) still creates the PR; it just omits Jira Fixes: links and the Jira follow-up comment.
Called from Phase 6 of the analyze-cve skill after Phase 5 verification succeeds.
Use this skill when:
REPO_DIR and verification passedembargo_status is not TrueDo not use this skill to apply the fix itself (that is Phase 5) or to post the analysis report (that is report-to-jira in Phase 4).
From the parent command:
| Input | Source | Required |
|---|---|---|
REPO_DIR | Phase 0.7 | Yes |
GIT_BRANCH | Phase 0.7 (mapped release branch, e.g. release-4.17) | Yes |
REPO_URL | Phase 0.7 | Yes |
CVE_ID | Phase 0.5 / direct CVE mode | Yes |
SOURCE_TICKET | --jira= / --jql= | No |
AUTO_APPROVE | Phase 0 (--auto-approve, default no) | Yes |
FORK_ORG | Caller environment (e.g. CI/RWS runner) | No — enables Fork Mode when set |
PHASE5_FILES | Phase 5 allowlist (incl. untracked) | Yes |
| Module path, old version, new version | Phase 4 / 5 | Yes if a dependency bump |
| Short CVE description | Phase 1 | Recommended |
| Phase 5 change summary | Phase 5 "Document Changes" | Yes |
Hard requirements for this skill. Phase 0 does not treat gh as required (warn-only). This skill must not assume either tool is present — re-check both, then fail if either is missing. Do not create a PR another way.
which git 2>/dev/null || echo "MISSING: git"
which gh 2>/dev/null || echo "MISSING: gh"
gh auth status 2>/dev/null || echo "MISSING: gh auth"git is missing → print install instructions and return status: failed (git_missing). Stop. Do not commit or push.gh is missing → print install instructions (https://cli.github.com/) and return status: failed (gh_missing). Stop. Do not commit or push.gh auth status fails → print gh auth login instructions and return status: failed (gh_unauthenticated). Stop. Do not commit or push.Credential rule: Never print, echo, or log tokens, PATs, or
ghauth output that contains credentials. Reference credentials only via environment variable names.
FORK_ORG set)By default (no FORK_ORG) this skill pushes the fix branch directly to the cloned repo's own origin and opens a same-repo branch→base PR. That requires the authenticated gh/git identity to have direct write access to every upstream repo image-repo-mapping might resolve to (e.g. openshift/cert-manager-operator, openshift/cert-manager, openshift/secrets-store-csi-driver-operator, ...). In an unattended CI/RWS runner that isn't a collaborator on all of those repos, that's the wrong default.
IF the environment variable FORK_ORG is set (e.g. a bot-owned org), push to a fork under that org instead and open a cross-repo PR — mirrors the fork/upstream model used elsewhere for automated OpenShift PRs. This section only changes where the branch is pushed and how the PR's --head is specified; Step 0 (safety gates), Step 1 (org/repo/base-branch resolution from origin), Step 2 (conflicting-PR detection), and Step 3b (commit, allowlist validation) are unchanged and still operate against REPO_DIR/ORG/REPO/BASE_BRANCH resolved from origin.
FORK_REPO="${FORK_ORG}/${REPO}" # REPO = repo name parsed from origin in Step 1, e.g. "cert-manager-operator"Never assume a repo at ${FORK_REPO} is the right push target just because the name matches — verify its fork lineage first. Reusing an unrelated (or unexpectedly renamed/transferred) repo that happens to occupy that name would push the fix branch and open a PR from somewhere unintended.
if FORK_JSON=$(gh repo view "${FORK_REPO}" --json isFork,parent 2>/dev/null); then
IS_FORK=$(echo "$FORK_JSON" | jq -r '.isFork')
PARENT_FULL_NAME=$(echo "$FORK_JSON" | jq -r '.parent.fullName // empty')
else
IS_FORK=""
PARENT_FULL_NAME=""
fi${FORK_REPO} exists (the gh repo view above succeeded) and (IS_FORK is not exactly true or PARENT_FULL_NAME is not exactly ${ORG}/${REPO}) → stop immediately, do not add the fork remote or push anything, and return status: failed (fork_wrong_lineage).${FORK_REPO} exists and lineage matches → skip fork creation below, continue to Step 3a.${FORK_REPO} does not exist → create it:gh repo fork "${ORG}/${REPO}" --org "${FORK_ORG}" --remote=false --clone=false --default-branch-only=false
FORK_CREATE_EXIT=$?FORK_CREATE_EXIT is non-zero → stop immediately and return status: failed (fork_create_failed). Do not poll, do not add the fork remote, do not push — the create call already reported failure, so there is nothing to wait for.FORK_CREATE_EXIT is 0 → the fork was accepted; poll briefly, since GitHub creates forks asynchronously:for i in $(seq 1 10); do
gh repo view "${FORK_REPO}" >/dev/null 2>&1 && break
sleep 3
donestatus: failed (fork_create_failed). Do not fall back to pushing to origin — that would silently switch to requiring direct upstream write access, which is exactly what FORK_ORG was set to avoid.Branch creation/naming is identical to the non-fork case below (BRANCH_NAME derived from CVE_ID/SOURCE_TICKET, checked out from BASE_BRANCH).
origingit -C "${REPO_DIR}" remote add fork "https://github.com/${FORK_REPO}.git" 2>/dev/null \
|| git -C "${REPO_DIR}" remote set-url fork "https://github.com/${FORK_REPO}.git"
# Keep the fork in sync with the upstream base branch before pushing a branch built on top of it,
# so the PR diff is just this fix — not a stale-fork drift diff.
git -C "${REPO_DIR}" fetch origin "${BASE_BRANCH}"
git -C "${REPO_DIR}" push fork "refs/remotes/origin/${BASE_BRANCH}:refs/heads/${BASE_BRANCH}" --force-with-lease 2>/dev/null || true
git -C "${REPO_DIR}" push -u fork "${BRANCH_NAME}"Never force-push ${BASE_BRANCH} on origin (the upstream repo) — the force-with-lease sync above only ever targets the fork remote's copy of that branch name, never origin.
Do not use gh pr create --head "${FORK_ORG}:${BRANCH_NAME}". gh pr create --head only supports a user-owned head repo via the owner:branch syntax — per gh pr create --help: "Using an organization as the owner is currently not supported" (tracked at cli/cli#10093). Since FORK_ORG may be a GitHub organization, use the REST API's head_repo parameter instead (via gh api), which unambiguously names the head repository regardless of whether FORK_ORG is a user or an org:
printf '%s' "${PR_BODY}" > "${WORK_CVE}/pr-body.md"
PR_URL=$(gh api \
"repos/${ORG}/${REPO}/pulls" \
-f head_repo="${FORK_REPO}" \
-f head="${BRANCH_NAME}" \
-f base="${BASE_BRANCH}" \
-f title="${PR_TITLE}" \
-F body=@"${WORK_CVE}/pr-body.md" \
--jq '.html_url')head is the bare branch name here (no owner: prefix) — disambiguation comes entirely from the separate head_repo field, so this works whether FORK_ORG/FORK_REPO is user- or org-owned. Everything else (title/body construction, stacked-PR update path) is unchanged; PR_URL is captured directly from the API response instead of gh pr create's output.
gh auth status in Prerequisites must reflect a token with public_repo (classic PAT) or equivalent fork/push/PR scope on ${FORK_ORG} and PR-creation scope against the upstream repo — this is set up by the caller before this skill runs (not part of this skill). Never echo the token itself.
IF embargo_status = True → abort immediately. Do not commit, push, create a PR, or mention ticket contents.
IF Phase 5 did not complete successfully → return status: skipped (phase5_incomplete).
Confirm there are local changes to commit:
git -C "${REPO_DIR}" status --porcelain
git -C "${REPO_DIR}" diff --statPHASE5_FILES (see the check in Step 3b) before skipping to Step 4. A clean worktree only means nothing is uncommitted; it does not prove the existing commit(s) contain only Phase 5 paths. IF the branch diff vs ${BASE_BRANCH} contains paths outside PHASE5_FILES → always return status: failed (phase5_files_mismatch) immediately. Never gated by AUTO_APPROVE and never prompt.${GIT_BRANCH} with no Phase 5 commit → return status: skipped (no_local_changes).IF AUTO_APPROVE=no (and the parent hasn't already recorded a yes) → Ask:
Phase 5 applied the fix locally. Create a GitHub PR against <GIT_BRANCH>?status: skipped (user_declined). Leave the working tree as-is.IF AUTO_APPROVE=yes → treat as yes, skip the prompt, continue. Still do not commit until Step 3.
git -C "${REPO_DIR}" remote get-url originParse ORG/REPO from the origin URL (github.com/openshift/hypershift.git → openshift/hypershift). If origin is SSH (git@github.com:org/repo.git), parse the same way.
BASE_BRANCH = GIT_BRANCH from Phase 0.7 (the mapped release branch the clone is on). Do not open the PR against main unless that is actually GIT_BRANCH.
Title match only. Do not inspect PR files, body, module versions, or go.mod diffs.
gh pr list --repo "${ORG}/${REPO}" --state open --base "${BASE_BRANCH}" \
--json number,title,url,headRefName,authorA PR is a match if its title contains (case-insensitive) either this CVE_ID, or SOURCE_TICKET (when set).
IF none → continue to Step 3 with strategy new.
IF any match:
AUTO_APPROVE=no → present the list and wait for the user:
Open PR(s) on <ORG/REPO> base <BASE_BRANCH> already have this CVE/Jira in the title:
#<N> <title> <url> head=<branch>
How should I proceed?
1. stack — check out that PR branch, commit this fix on top, push (PR updates)
2. wait — stop; do not open a competing PR
3. independent — new branch from <BASE_BRANCH> (may need rebase later)| Choice | Action |
|---|---|
stack | git fetch origin <headRefName> && git checkout <headRefName> && git pull. Re-apply leftover Phase 5 changes if they are not already on that branch (same method as Phase 5 — bump, patch, or config edit). Strategy = stack. |
wait | Return status: skipped (user_wait_for_pr) including the existing PR URL. |
independent | Warn that a second PR on the same base may need rebase later. If go.mod is in this diff, mention possible module-file conflicts. Strategy = new. |
| No answer | Do not guess. Ask again. |
AUTO_APPROVE=yes → always wait, automatically, with no exceptions: return status: skipped (user_wait_for_pr_auto) including the existing PR URL. Never auto-choose stack (that pushes commits onto a branch nobody reviewed for this run) or independent (that opens a second, possibly duplicate/conflicting PR) unattended. Log clearly that this run stopped because of the existing PR, so a human can revisit with --auto-approve=no if a different resolution is actually wanted.
Work only inside REPO_DIR.
Vendor: Phase 5 already ran go mod vendor/make vendor (if applicable) and included any resulting paths in PHASE5_FILES before verification. Do not run vendor sync here — doing so after PHASE5_FILES was written would generate paths that never make it into the allowlist and get silently dropped from the commit. IF a dependency bump is present in PHASE5_FILES but vendor/ looks out of sync (e.g. go mod vendor reports diffs) → stop and tell the user to re-run Phase 5, rather than fixing it here.
New PR (strategy = new):
git -C "${REPO_DIR}" fetch origin "${BASE_BRANCH}"
git -C "${REPO_DIR}" checkout "${BASE_BRANCH}"
git -C "${REPO_DIR}" pull --ff-only origin "${BASE_BRANCH}"
SLUG="$(echo "${CVE_ID}" | tr '[:upper:]' '[:lower:]')"
if [ -n "${SOURCE_TICKET}" ]; then
BRANCH_NAME="fix/${SLUG}-$(echo "${SOURCE_TICKET}" | tr '[:upper:]' '[:lower:]')"
else
BRANCH_NAME="fix/${SLUG}"
fi
git -C "${REPO_DIR}" checkout -b "${BRANCH_NAME}"Re-apply the Phase 5 changes on this branch if checking out BASE_BRANCH discarded uncommitted work. Prefer not discarding: stash before checkout if the working tree is dirty, then stash pop onto BRANCH_NAME.
Stack (strategy = stack): already on the existing PR branch. Set BRANCH_NAME to that head ref. Do not create a new branch.
Stage only PHASE5_FILES, not the whole worktree. That allowlist is written in Phase 5 (phase5-files.txt) and includes new untracked files. go.mod, go.sum, go.work, and vendor/ are common for dependency version bumps, but other CVE remediations may only touch source, config, Dockerfiles, or scripts.
WORK_CVE is in the command's own workspace (.work/), not inside REPO_DIR. Paths in the allowlist are relative to REPO_DIR.
WORK_CVE="${AI_HELPERS_WORKSPACE:-.}/.work/compliance/analyze-cve/${CVE_ID}"
PHASE5_FILES="${WORK_CVE}/phase5-files.txt"IF phase5-files.txt is missing or empty → rebuild it from Phase 5 "Document Changes" (and phase5-before.status if present). IF the allowlist still cannot be determined → always return status: failed (phase5_files_missing) immediately. Never gated by AUTO_APPROVE and never prompt. Do not fall back to whole-worktree git diff / git add -A / git add . in any case.
# inspect current tree for sanity, but do not use it as the stage set
git -C "${REPO_DIR}" status --porcelain
while IFS= read -r f; do
[ -n "${f}" ] || continue
git -C "${REPO_DIR}" add -- "${f}"
done < "${PHASE5_FILES}"git add -- <path> stages modifications, deletions, and untracked files. Examples of what belongs on the allowlist:
go.mod, go.sum, and go.work / vendor/ only if Phase 5 changed themDo not add .work/, analysis reports, or credentials. Do not add pre-existing dirty files that are absent from PHASE5_FILES. Do not git add go.mod / vendor/ unless they are on the allowlist.
Validate the staged set exactly matches the allowlist. The loop above only adds allowlisted paths — it does not prove the index is free of anything else (e.g. leftover pre-existing staged files). Diff the two sets and reject extras instead of committing them silently:
git -C "${REPO_DIR}" diff --cached --name-only > "${WORK_CVE}/staged-files.txt"
EXTRA="$(comm -23 <(sort "${WORK_CVE}/staged-files.txt") <(sort "${PHASE5_FILES}"))"
if [ -n "${EXTRA}" ]; then
echo "Unstaging paths not in PHASE5_FILES:"
echo "${EXTRA}"
while IFS= read -r x; do
[ -n "${x}" ] || continue
git -C "${REPO_DIR}" restore --staged -- "${x}"
done <<< "${EXTRA}"
fi
MISSING="$(comm -23 <(sort "${PHASE5_FILES}") <(sort "${WORK_CVE}/staged-files.txt"))"
if [ -n "${MISSING}" ]; then
echo "ERROR: PHASE5_FILES paths missing from the staged set:"
echo "${MISSING}"
exit 1 # return status: failed (phase5_files_incomplete)
fiIF any path was rejected → tell the user which paths were unstaged and why before continuing. Re-run the diff after unstaging to confirm the index now matches PHASE5_FILES exactly (accounting for deletions).
On a stack branch, or when Step 0 skipped ahead because a Phase 5 commit already existed, run the equivalent check against the branch, not just the index:
git -C "${REPO_DIR}" diff --name-only "${BASE_BRANCH}...HEAD" > "${WORK_CVE}/branch-files.txt"
comm -23 <(sort "${WORK_CVE}/branch-files.txt") <(sort "${PHASE5_FILES}")IF that reports any path outside PHASE5_FILES → stop; do not push or open/update the PR. Always return status: failed (phase5_files_mismatch) immediately, for manual follow-up. Never gated by AUTO_APPROVE and never prompt.
Commit message. Match the actual Phase 5 change, not a canned module bump. Use the repo's own convention when detectable — e.g. many OpenShift repos expect UPSTREAM: <upstream-pr-or-carry>: <subject>:
git -C "${REPO_DIR}" log --oneline -20If the recent history shows the UPSTREAM: convention and a dependency bump with a known upstream fix PR number (from the Phase 4 remediation plan):
UPSTREAM: <upstream-pr>: Bump <module> to <new> for <CVE_ID>If a dependency bump that is downstream-only / fork / carry (no tracked upstream PR, but repo uses UPSTREAM: convention):
UPSTREAM: <carry>: Bump <module> to <new> for <CVE_ID>If the repo does not use the UPSTREAM: convention, use a plain subject. Dependency bump:
Bump <module> to <new> for <CVE_ID>If the fix is not a version bump (code, config, workaround), regardless of convention:
Fix <CVE_ID>: <short description of the change>Body: one or two sentences describing what changed and why. Include Fixes: <SOURCE_TICKET> in Jira mode; omit that line in direct CVE mode. For a bump, include old → new versions. Do not claim a module bump if Phase 5 did not bump a module.
git -C "${REPO_DIR}" commit --signoff -m "${COMMIT_SUBJECT}" -m "${COMMIT_BODY}"Sign off (DCO) by default — most upstream/downstream OpenShift-style repos require it and a missing sign-off is a common, avoidable PR-check failure. Never use --no-verify or skip hooks unless the user explicitly asks.
git -C "${REPO_DIR}" push -u origin "${BRANCH_NAME}"Default is a normal push. Amending an existing commit and force-pushing is never authorized by AUTO_APPROVE; it always requires an interactive user (moot in practice, since AUTO_APPROVE=yes never selects stack — see Step 2).
Dependency bump:
<CVE_ID>: bump <module-short> to <new> [<SOURCE_TICKET>]Omit [<SOURCE_TICKET>] in direct CVE mode. Example: CVE-2026-33186: bump google.golang.org/grpc to v1.79.3 [OCPBUGS-80452]
Non-bump fix:
<CVE_ID>: <short description> [<SOURCE_TICKET>]Read the repo's PR template if one exists (cat "${REPO_DIR}/.github/PULL_REQUEST_TEMPLATE.md" 2>/dev/null) and structure the body around it. Otherwise use:
Jira mode — every Jira key must be a markdown link. Use a Fixes: line (do not use a bare key):
## Summary
Fixes: [<SOURCE_TICKET>](<JIRA_BASE_URL>/browse/<SOURCE_TICKET>)
<What Phase 5 changed and why — e.g. bump `<module>` from `<old>` to `<new>`, or a code/config workaround.>
<2–3 sentence CVE description from Phase 1>
## Changes
- <file- or behavior-level bullets from the Phase 5 diff>
## Verification
- `make verify` / `go mod verify` (if modules changed)
- `make build` / `go build ./...`
- `govulncheck ./...`
---
Always review AI generated responses prior to use.
AI-assisted change via compliance plugin (`/compliance:analyze-cve`)Direct CVE mode — same body without the Fixes: line and without Jira URLs.
Do not include hostnames, cluster names, routes, usernames, passwords, or tokens in the title, body, or comments.
New PR (default mode — no FORK_ORG):
gh pr create \
--repo "${ORG}/${REPO}" \
--base "${BASE_BRANCH}" \
--head "${BRANCH_NAME}" \
--title "${PR_TITLE}" \
--body "${PR_BODY}"New PR (fork mode — FORK_ORG set): use the Fork Mode Step 4 variant (gh api repos/.../pulls -f head_repo=...) instead of the gh pr create call above.
Stacked on existing PR: push is enough (to origin by default, or fork in Fork Mode). Optionally:
gh pr comment "${EXISTING_PR}" --repo "${ORG}/${REPO}" \
--body "Added <CVE_ID> fix: <what Phase 5 changed>."And gh pr edit to append the CVE / Jira key to title and the Fixes: line if missing.
Capture PR_URL from gh pr create output or gh pr view --json url (default mode), or from the gh api response (Fork Mode).
IF create fails (permissions, fork needed) → show the exact gh error (no secrets). If the error indicates missing write access and FORK_ORG is not set, suggest re-running with FORK_ORG configured (see Fork Mode) rather than guessing at a fork target. Otherwise give the user the title/body to paste, return status: failed (pr_create_failed). Do not retry more than once.
Skip this step if SOURCE_TICKET is unset (direct CVE mode) → still status: success for the GitHub PR.
Use the report-to-jira skill's Follow-up: PR URL comment section. Do not edit or replace the Phase 4 analysis comment. Do not add/remove labels here.
IF posting fails → display the comment in session for manual paste. The GitHub PR is still a success (jira_followup: failed).
Success:
{
"skill": "create-fix-pr",
"status": "success",
"action": "created | updated",
"pr_url": "https://github.com/<org>/<repo>/pull/<n>",
"pr_number": 123,
"branch": "fix/cve-yyyy-nnnnn-ocpbugs-12345",
"base_branch": "release-4.17",
"cve_id": "CVE-YYYY-NNNNN",
"source_ticket": "OCPBUGS-12345 or null",
"jira_followup": "posted | skipped | failed"
}Skipped:
{
"skill": "create-fix-pr",
"status": "skipped",
"reason": "user_declined | no_local_changes | phase5_incomplete | user_wait_for_pr | user_wait_for_pr_auto | embargo"
}Failed:
{
"skill": "create-fix-pr",
"status": "failed",
"reason": "git_missing | gh_missing | gh_unauthenticated | pr_create_failed | commit_failed | phase5_files_missing | phase5_files_mismatch | fork_wrong_lineage | fork_create_failed",
"error": "<message without secrets>"
}Called from Phase 6 of the analyze-cve skill after Phase 5 verification succeeds. Runs after the Repo Guard has confirmed REPO_DIR still exists.
AUTO_APPROVE=yes recorded once upfrontBASE_BRANCH / main / master / release-*PHASE5_FILES; never git add -A or git add .stack, the branch) path set against PHASE5_FILES and rejecting extrasPHASE5_FILES is writtenFORK_ORG repo without first verifying its fork lineage (isFork + parent.fullName match) — never reuse a same-named repo that isn't actually a fork of the targetAUTO_APPROVE=yes auto-select stack or independent on a PR-title conflict — always wait unattendedAUTO_APPROVE=yes skip a hard-fail case (missing/mismatched PHASE5_FILES, ambiguous conflict resolution) — those always require a human, flag or not© openshift-eng, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in plugins/compliance/skills/create-fix-pr of openshift-eng/ai-helpers.
Open the folder on GitHubat commit a627176
Create Fix PR next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Create Fix PR this skillopenshift-eng/ai-helpers | 120 | — | ~6.2k | Automated safety check: Pass | Apache-2.0 | |
| Security AdvisoryMidnightBSD/src | 114 | — | ~2.2k | Automated safety check: Pass | Custom licence | |
| Security Vulnerability Analysiseclipse-ankaios/ankaios | 125 | — | ~1.5k | Automated safety check: Pass | Apache-2.0 | |
| Warp Vulnerability Triagewarpdotdev/warp | 65k | 1 repos | ~2.1k | Automated safety check: Pass | AGPL-3.0 | |
| Cve Doctorgetlago/lago-front | 163 | — | ~2.9k | Automated safety check: Pass | MIT | |
| Deal With Security Advisorypaperclipai/paperclip | 99k | — | ~2k | Automated safety check: Pass | MIT |
MidnightBSD/src
Handle a security fix end to end for MidnightBSD src - triage a FreeBSD security advisory (FreeBSD-SA-) or CVE against this tree, port the fix to master and both stable branches, add the UPDATING…
eclipse-ankaios/ankaios
Analyze potential Ankaios security vulnerabilities from pasted reports, local evidence, or advisory URLs.
warpdotdev/warp
Gathers security findings from Dependabot, GCP container scanning, Docker Scout and Linear security issues, then triages and remediates them across Warp's repos and images.
getlago/lago-front
Triage a CVE / Dependabot alert in a JS/TS project and recommend the least-invasive fix.
paperclipai/paperclip
Handle confidential GitHub Security Advisory response for Paperclip.
boostsecurityio/poutine
Run snapshot regression tests after changes to OPA rules, scanners, analyzers, or formatters to detect output regressions.
openshift-eng/ai-helpers
Find and independently validate actionable reliability defects across OpenShift release jobs and presubmits, then export portable issue handoffs.
openshift-eng/ai-helpers
Fetch and address all PR review comments — categorize by priority, make code changes, post replies, and push.
openshift-eng/ai-helpers
Categorize Jira issues into Red Hat Sankey Activity Type categories using MCP Jira tools.
openshift-eng/ai-helpers
Decide whether a GitHub PR has unanswered authorized review comments or new required CI failures worth a follow-up agent.
openshift-eng/ai-helpers
Analyze OpenShift must-gather diagnostic data including cluster operators, pods, nodes, and network components.
openshift-eng/ai-helpers
Schema for the autodl JSON data file produced by payload-analysis for database ingestion — you must use this skill whenever generating the autodl JSON file
Categories
A skill your agent uses when opening or updating a GitHub pull request after Phase 5 of /compliance:analyze-cve has applied and verified a CVE fix locally. Create Fix PR is an agent skill from openshift-eng/ai-helpers. Use when opening or updating a GitHub pull request after Phase 5 of /compliance:analyze-cve has applied and verified a CVE fix locally.
Create Fix PR fits situations like: updating a GitHub pull request after Phase 5 of /compliance:analyze-cve has applied and verified a CVE fix locally; tasks that involve Vulnerability scanning.
Run `npx skills add openshift-eng/ai-helpers --skill create-fix-pr -a claude-code`. Or copy the skill folder (plugins/compliance/skills/create-fix-pr in openshift-eng/ai-helpers) into .claude/skills/create-fix-pr in your project. Claude Code loads it when a task matches its description.
Run `npx skills add openshift-eng/ai-helpers --skill create-fix-pr -a codex`. Or copy the skill folder (plugins/compliance/skills/create-fix-pr in openshift-eng/ai-helpers) into .agents/skills/create-fix-pr in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add openshift-eng/ai-helpers --skill create-fix-pr -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/create-fix-pr, .gemini/skills/create-fix-pr, .github/skills/create-fix-pr and .opencode/skills/create-fix-pr in your project.
Going by SKILL.md and its folder, Create Fix PR needs the command-line tools its instructions call (git, gh, jq, go and make).
SKILL.md names 2 domains. In commands or code: github.com and cli.github.com; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Create Fix PR is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.2k tokens (SKILL.md is roughly 25k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Create Fix PR: Security Advisory (MidnightBSD/src, 114 stars), Security Vulnerability Analysis (eclipse-ankaios/ankaios, 125 stars), Warp Vulnerability Triage (warpdotdev/warp, 65k stars) and Cve Doctor (getlago/lago-front, 163 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
openshift-eng (a GitHub organization) maintains it in openshift-eng/ai-helpers, which has 120 GitHub stars. The repository holds 118 skills in this directory. The repository was last updated on October 6, 2026.
Source: openshift-eng/ai-helpers on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.