Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/renovate-cve-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Renovate config for CVE Remediation Controller. Env: RENOVATE_TARGET_REPO, RENOVATE_BASE_BRANCH, RENOVATE_PACKAGE_NAME, RENOVATE_CVE_ID.

const prBody = `## Security Update

This PR fixes **${process.env.RENOVATE_CVE_ID || 'CVE'}** by updating \`${process.env.RENOVATE_PACKAGE_NAME || 'package'}\`.

---
*Created by [CVE Remediation Controller](https://github.com/project-codeflare/codeflare-operator/blob/main/.github/workflows/cve-controller.yml)*
`;

module.exports = {
platform: 'github',
onboarding: false,
requireConfig: 'ignored',

repositories: [process.env.RENOVATE_TARGET_REPO].filter(Boolean),
baseBranches: [process.env.RENOVATE_BASE_BRANCH].filter(Boolean),
enabledManagers: ['gomod'],

packageRules: [
{
matchPackagePatterns: [process.env.RENOVATE_PACKAGE_NAME].filter(Boolean),
enabled: true,
recreateWhen: 'always',
rebaseWhen: 'behind-base-branch',
prPriority: 99,
labels: ['security', 'cve-fix', process.env.RENOVATE_CVE_ID].filter(Boolean),
},
{
matchPackagePatterns: ['*'],
excludePackagePatterns: [process.env.RENOVATE_PACKAGE_NAME].filter(Boolean),
enabled: false,
},
],

prTitle: `fix(security): Update ${process.env.RENOVATE_PACKAGE_NAME || 'package'} [${process.env.RENOVATE_CVE_ID || 'CVE'}]`,
prBody: prBody,
branchPrefix: 'cve-fix/',
branchName: `cve-fix/${process.env.RENOVATE_CVE_ID || 'cve'}-${(process.env.RENOVATE_PACKAGE_NAME || 'pkg').split('/').pop()}`.toLowerCase().replace(/[^a-z0-9-/]/g, '-'),

dependencyDashboard: false,
prHourlyLimit: 0,
prConcurrentLimit: 0,
branchConcurrentLimit: 0,
commitMessagePrefix: 'fix(security):',
postUpdateOptions: ['gomodTidy'],
logLevel: process.env.LOG_LEVEL || 'info',
};
234 changes: 234 additions & 0 deletions .github/workflows/cve-controller.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
# CVE Remediation Controller: scan stream branch for a CVE; if affected, open fix PR via Renovate, then recheck PR and close with comment if still vulnerable.
# Trigger: workflow_dispatch with cve_id and base_branch. Secret: CVE_CONTROLLER_PAT (repo scope).

name: CVE Remediation Controller

on:
workflow_dispatch:
inputs:
cve_id:
description: 'CVE identifier (e.g., CVE-2024-12345)'
required: true
type: string
base_branch:
description: 'Base branch to scan and target for fix (e.g., rhoai-2.16)'
required: true
type: string
repo_override:
description: 'E2E only: repo instead of prod'
required: false
type: string
force_recheck_fail:
description: 'E2E only: force recheck to fail'
required: false
type: string

concurrency:
group: cve-operator-${{ github.event.inputs.repo_override || 'prod' }}-${{ github.event.inputs.cve_id }}-${{ github.event.inputs.base_branch }}
cancel-in-progress: false

env:
TARGET_REPO: ${{ github.event.inputs.repo_override || 'red-hat-data-services/codeflare-operator' }}
CONFIG_REPO: ${{ github.event.inputs.repo_override || 'project-codeflare/codeflare-operator' }}

jobs:
scan:
name: Scan for CVE
runs-on: ubuntu-latest
outputs:
vulnerable: ${{ steps.scan.outputs.vulnerable }}
package_name: ${{ steps.scan.outputs.package_name }}
fix_branch: ${{ steps.branch.outputs.fix_branch }}

steps:
- name: Generate fix branch name
id: branch
run: |
CVE_ID="${{ github.event.inputs.cve_id }}"
BASE_BRANCH="${{ github.event.inputs.base_branch }}"
FIX_BRANCH="cve-fix/${CVE_ID}-${BASE_BRANCH}"
FIX_BRANCH=$(echo "$FIX_BRANCH" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9./-]/-/g')
echo "fix_branch=${FIX_BRANCH}" >> $GITHUB_OUTPUT

- name: Create fix branch from base branch
run: |
FIX_BRANCH="${{ steps.branch.outputs.fix_branch }}"
BASE_BRANCH="${{ github.event.inputs.base_branch }}"
git clone https://x-access-token:${{ secrets.CVE_CONTROLLER_PAT }}@github.com/${{ env.TARGET_REPO }}.git repo
cd repo
git config user.email "cve-controller@github.com"
git config user.name "CVE Controller"
git fetch origin "${BASE_BRANCH}"
if git ls-remote --heads origin "${FIX_BRANCH}" | grep -q .; then
echo "Branch ${FIX_BRANCH} already exists"
else
git checkout -b "${FIX_BRANCH}" "origin/${BASE_BRANCH}"
git push origin "${FIX_BRANCH}"
fi

- name: Checkout fix branch
uses: actions/checkout@v4
with:
repository: ${{ env.TARGET_REPO }}
ref: ${{ steps.branch.outputs.fix_branch }}
token: ${{ secrets.CVE_CONTROLLER_PAT }}

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: './go.mod'

- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest

- name: Scan source for CVE
id: scan
run: |
set +e
CVE_ID="${{ github.event.inputs.cve_id }}"
GOVOUT=$(govulncheck ./... 2>&1)
echo "$GOVOUT"
if echo "$GOVOUT" | grep -qi "${CVE_ID}"; then
echo "vulnerable=true" >> $GITHUB_OUTPUT
PKG=$(echo "$GOVOUT" | grep -A 10 -i "${CVE_ID}" | grep "Module:" | head -1 | sed 's/.*Module: //')
[ -z "$PKG" ] && PKG=$(echo "$GOVOUT" | grep "Module:" | head -1 | sed 's/.*Module: //')
echo "package_name=${PKG:-unknown}" >> $GITHUB_OUTPUT
else
echo "vulnerable=false" >> $GITHUB_OUTPUT
echo "package_name=" >> $GITHUB_OUTPUT
fi

- name: Summary - No CVE detected
if: steps.scan.outputs.vulnerable == 'false'
run: echo "CVE ${{ github.event.inputs.cve_id }} not detected. Workflow complete."

fix:
name: Create Fix PR
needs: scan
if: needs.scan.outputs.vulnerable == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout (Renovate config)
uses: actions/checkout@v4
with:
repository: ${{ env.CONFIG_REPO }}
token: ${{ secrets.CVE_CONTROLLER_PAT }}

- name: Run Renovate
uses: renovatebot/github-action@v46.0.1
with:
configurationFile: .github/renovate-cve-config.js
token: ${{ secrets.CVE_CONTROLLER_PAT }}
env:
RENOVATE_TARGET_REPO: ${{ env.TARGET_REPO }}
RENOVATE_BASE_BRANCH: ${{ needs.scan.outputs.fix_branch }}
RENOVATE_PACKAGE_NAME: ${{ needs.scan.outputs.package_name }}
RENOVATE_CVE_ID: ${{ github.event.inputs.cve_id }}
RENOVATE_COMPONENT: codeflare-operator
RENOVATE_SCAN_TYPE: go

recheck:
name: Recheck PR
needs: [fix, scan]
if: needs.scan.outputs.vulnerable == 'true'
runs-on: ubuntu-latest
outputs:
pr_number: ${{ steps.pr.outputs.number }}
head_ref: ${{ steps.pr.outputs.head_ref }}
steps:
- name: Find Renovate PR
id: pr
run: |
FIX_BRANCH="${{ needs.scan.outputs.fix_branch }}"
for i in $(seq 1 12); do
PR_JSON=$(gh pr list --repo ${{ env.TARGET_REPO }} --base "${FIX_BRANCH}" --state open --json number,headRefName --limit 1)
if [ -n "$PR_JSON" ] && [ "$PR_JSON" != "[]" ]; then
echo "number=$(echo "$PR_JSON" | jq -r '.[0].number')" >> $GITHUB_OUTPUT
echo "head_ref=$(echo "$PR_JSON" | jq -r '.[0].headRefName')" >> $GITHUB_OUTPUT
exit 0
fi
sleep 10
done
echo "number=" >> $GITHUB_OUTPUT
echo "head_ref=" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.CVE_CONTROLLER_PAT }}

- name: Checkout PR branch
if: steps.pr.outputs.number != ''
uses: actions/checkout@v4
with:
repository: ${{ env.TARGET_REPO }}
ref: ${{ steps.pr.outputs.head_ref }}
token: ${{ secrets.CVE_CONTROLLER_PAT }}

- name: Setup Go
if: steps.pr.outputs.number != ''
uses: actions/setup-go@v5
with:
go-version-file: './go.mod'

- name: Install govulncheck
if: steps.pr.outputs.number != ''
run: go install golang.org/x/vuln/cmd/govulncheck@latest

- name: Recheck CVE on PR branch
if: steps.pr.outputs.number != ''
id: recheck
run: |
CVE_ID="${{ github.event.inputs.cve_id }}"
if [ "${{ github.event.inputs.force_recheck_fail }}" = "true" ]; then
echo "still_vulnerable=true" >> $GITHUB_OUTPUT
exit 0
fi
set +e
GOVOUT=$(govulncheck ./... 2>&1)
echo "$GOVOUT"
if echo "$GOVOUT" | grep -qi "${CVE_ID}"; then
echo "still_vulnerable=true" >> $GITHUB_OUTPUT
else
echo "still_vulnerable=false" >> $GITHUB_OUTPUT
fi

- name: Close PR - CVE still present
if: steps.pr.outputs.number != '' && steps.recheck.outputs.still_vulnerable == 'true'
run: |
gh pr close ${{ steps.pr.outputs.number }} --repo ${{ env.TARGET_REPO }} \
--comment "Recheck failed: this change did not fix **${{ github.event.inputs.cve_id }}**. Please fix manually."
env:
GH_TOKEN: ${{ secrets.CVE_CONTROLLER_PAT }}

lint:
name: Lint Renovate PR
needs: [recheck, scan]
if: needs.scan.outputs.vulnerable == 'true' && needs.recheck.outputs.pr_number != ''
runs-on: ubuntu-latest
container:
image: quay.io/opendatahub/pre-commit-go-toolchain:v0.2
env:
XDG_CACHE_HOME: /cache
GOCACHE: /cache/go-build
GOMODCACHE: /cache/go-mod
PRE_COMMIT_HOME: /cache/pre-commit
volumes:
- /cache
steps:
- uses: actions/checkout@v4
with:
repository: ${{ env.TARGET_REPO }}
ref: ${{ needs.recheck.outputs.head_ref }}
token: ${{ secrets.CVE_CONTROLLER_PAT }}

- name: Set Go
uses: actions/setup-go@v5
with:
go-version-file: './go.mod'

- name: Cache
uses: actions/cache@v4
with:
path: /cache
key: ${{ runner.os }}-cache-${{ hashFiles('**/go.sum', '.pre-commit-config.yaml') }}

- name: Run pre-commit checks
run: pre-commit run --show-diff-on-failure --color=always --all-files
Loading