mirror of
https://github.com/krkn-chaos/krkn.git
synced 2026-04-15 06:57:28 +00:00
fix: prevent script injection in require-docs workflow (#1187)
- replace shell interpolation of PR body with jq + $GITHUB_EVENT_PATH - replace shell interpolation of branch name with actions/github-script - remove unused actions/checkout step - add 27 unit tests covering checkbox detection, docs PR search, and security regression checks to prevent re-introduction of the bug Signed-off-by: Arpit Raj <vrxn.arp1traj@gmail.com> Co-authored-by: Paige Patton <64206430+paigerube14@users.noreply.github.com>
This commit is contained in:
58
.github/workflows/require-docs.yml
vendored
58
.github/workflows/require-docs.yml
vendored
@@ -9,37 +9,47 @@ jobs:
|
||||
name: Check Documentation Update
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check if Documentation is Required
|
||||
id: check_docs
|
||||
run: |
|
||||
echo "Checking PR body for documentation checkbox..."
|
||||
# Read the PR body from the GitHub event payload
|
||||
if echo "${{ github.event.pull_request.body }}" | grep -qi '\[x\].*documentation needed'; then
|
||||
# Read PR body from the event JSON file — never from shell interpolation.
|
||||
# jq handles all escaping; the shell never sees the user-controlled string.
|
||||
if jq -r '.pull_request.body // ""' "$GITHUB_EVENT_PATH" | \
|
||||
grep -qi '\[x\].*documentation needed'; then
|
||||
echo "Documentation required detected."
|
||||
echo "docs_required=true" >> $GITHUB_OUTPUT
|
||||
echo "docs_required=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Documentation not required."
|
||||
echo "docs_required=false" >> $GITHUB_OUTPUT
|
||||
echo "docs_required=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Enforce Documentation Update (if required)
|
||||
if: steps.check_docs.outputs.docs_required == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Retrieve feature branch and repository owner from the GitHub context
|
||||
FEATURE_BRANCH="${{ github.head_ref }}"
|
||||
REPO_OWNER="${{ github.repository_owner }}"
|
||||
WEBSITE_REPO="website"
|
||||
echo "Searching for a merged documentation PR for feature branch: $FEATURE_BRANCH in $REPO_OWNER/$WEBSITE_REPO..."
|
||||
MERGED_PR=$(gh pr list --repo "$REPO_OWNER/$WEBSITE_REPO" --state merged --json headRefName,title,url | jq -r \
|
||||
--arg FEATURE_BRANCH "$FEATURE_BRANCH" '.[] | select(.title | contains($FEATURE_BRANCH)) | .url')
|
||||
if [[ -z "$MERGED_PR" ]]; then
|
||||
echo ":x: Documentation PR for branch '$FEATURE_BRANCH' is required and has not been merged."
|
||||
exit 1
|
||||
else
|
||||
echo ":white_check_mark: Found merged documentation PR: $MERGED_PR"
|
||||
fi
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const featureBranch = context.payload.pull_request.head.ref;
|
||||
const repoOwner = context.repo.owner;
|
||||
const websiteRepo = 'website';
|
||||
|
||||
core.info(`Searching for a merged documentation PR for feature branch: ${featureBranch} in ${repoOwner}/${websiteRepo}...`);
|
||||
|
||||
const { data: pulls } = await github.rest.pulls.list({
|
||||
owner: repoOwner,
|
||||
repo: websiteRepo,
|
||||
state: 'closed',
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const mergedPr = pulls.find(
|
||||
(pr) => pr.merged_at && pr.title.includes(featureBranch)
|
||||
);
|
||||
|
||||
if (!mergedPr) {
|
||||
core.setFailed(
|
||||
`❌ Documentation PR for branch '${featureBranch}' is required and has not been merged.`
|
||||
);
|
||||
} else {
|
||||
core.info(`✅ Found merged documentation PR: ${mergedPr.html_url}`);
|
||||
}
|
||||
Reference in New Issue
Block a user