Skip to content

feat: add compliance-remediate.sh — close the audit -> auto-fix -> PR loop#220

Open
don-petry wants to merge 44 commits into
mainfrom
claude/issue-35-20260508-1731
Open

feat: add compliance-remediate.sh — close the audit -> auto-fix -> PR loop#220
don-petry wants to merge 44 commits into
mainfrom
claude/issue-35-20260508-1731

Conversation

@don-petry
Copy link
Copy Markdown
Contributor

@don-petry don-petry commented May 8, 2026

Summary

Adds scripts/compliance-remediate.sh to close the audit → report → auto-fix → PR loop from #35. The script reads findings.json from compliance-audit.sh and applies automatic remediations.

Closes #35

Direct API remediations (applied immediately, no PR)

Finding Fix
has_wiki=true PATCH has_wiki=false
allow_auto_merge=false PATCH allow_auto_merge=true
delete_branch_on_merge=false PATCH delete_branch_on_merge=true
has_discussions=false PATCH has_discussions=true
check-suite-auto-trigger-<app_id> Disable Claude/CodeRabbit auto-trigger (blocks auto-merge)
missing-label-<name> gh label create with correct colors/descriptions per standard

PR-based remediations (creates branch + PR in target repo)

Finding Fix
missing-codeowners, codeowners-empty, codeowners-org-leads-not-first, codeowners-individual-users, codeowners-no-catchall Creates/updates .github/CODEOWNERS with * @petry-projects/org-leads per current standard
unpinned-actions-<file.yml> Resolves tag to commit SHA (handles annotated tags), patches file, opens PR

Skipped with explanation (for human/agent pickup)

Missing workflow files, rulesets, dependabot.yml, CLAUDE.md/AGENTS.md, CodeQL default setup, push-protection settings

Usage

FINDINGS_FILE=/path/to/findings.json bash scripts/compliance-remediate.sh
DRY_RUN=true FINDINGS_FILE=/path/to/findings.json bash scripts/compliance-remediate.sh

Workflow integration snippet

Add a remediate job to compliance-audit-and-improvement.yml after the audit job:

remediate:
  name: Auto-Remediate Findings
  needs: audit
  if: needs.audit.result == 'success'
  runs-on: ubuntu-latest
  permissions:
    contents: write
    pull-requests: write
  env:
    GH_TOKEN: ${{ secrets.ORG_SCORECARD_TOKEN }}
  steps:
    - uses: actions/checkout@<sha> # v4
    - uses: actions/download-artifact@<sha> # v4
      with:
        name: compliance-report
        path: ${{ runner.temp }}/compliance-report
    - run: |
        FINDINGS_FILE="${{ runner.temp }}/compliance-report/findings.json" \
        REPORT_DIR="${{ runner.temp }}/remediation-report" \
        bash scripts/compliance-remediate.sh

Note: The workflow file itself cannot be committed by this agent (requires workflow token scope). Add the snippet above using a PAT with workflow scope.

Note on check-suite auto-trigger: That PATCH endpoint requires a classic PAT (ghp_*), not an OAuth token. If ORG_SCORECARD_TOKEN is an OAuth token, those findings will fail and should be remediated with scripts/fix-check-suite-prefs.sh instead.

Improvements over prior attempts (claude/issue-35-20260406-0341)

  • CODEOWNERS: Now generates * @petry-projects/org-leads (not individual users — forbidden per codeowners-standard.md updated 2026-05-04)
  • CODEOWNERS variants: Handles all 5 CODEOWNERS finding types, not just missing-codeowners
  • in-progress label: Added to label map (was missing in prior versions)
  • Check-suite auto-trigger: New remediation handler for check-suite-auto-trigger-* findings that block auto-merge
  • Bash 4+ guard: Consistent with apply-repo-settings.sh

Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added automated compliance remediation capability supporting both direct fixes and pull request-based changes. Includes dry-run mode for previewing changes, detailed reporting of remediation activities, and handles repository configurations, label management, code ownership files, and dependency pinning updates.

Review Change Stack

Copilot AI review requested due to automatic review settings May 8, 2026 17:37
@don-petry don-petry requested a review from a team as a code owner May 8, 2026 17:37
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Warning

Review limit reached

@don-petry, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 55 minutes and 9 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f9fc726d-d23e-407d-a879-069f1c713eee

📥 Commits

Reviewing files that changed from the base of the PR and between b9419cb and d07034b.

📒 Files selected for processing (2)
  • .gitignore
  • scripts/compliance-remediate.sh
📝 Walkthrough

Walkthrough

A new Bash automation script reads compliance findings from JSON and performs repository auto-remediation via GitHub CLI. It supports direct API updates and PR-based fixes, maintains per-run reports, tracks counters, and supports DRY_RUN mode. Handlers cover repository settings, check-suite preferences, labels, CODEOWNERS generation, and GitHub Actions workflow SHA pinning.

Changes

Compliance Remediation Script

Layer / File(s) Summary
Script Foundation & Configuration
scripts/compliance-remediate.sh
Strict mode, environment variable validation (ORG, FINDINGS_FILE, REPORT_DIR, DRY_RUN), report file paths, and logging helper functions (info, ok, warn, skip, err).
Reporting & State Tracking
scripts/compliance-remediate.sh
In-memory counters (direct, PR, skipped, failed) and report writer functions that log each remediation outcome to dedicated report files.
Basic Remediation Handlers
scripts/compliance-remediate.sh
remediate_setting() PATCHes repository settings via GitHub API; remediate_check_suite_auto_trigger() disables check-suite auto-trigger; both support DRY_RUN and record outcomes.
Label Creation Handlers
scripts/compliance-remediate.sh
remediate_label() creates labels with predefined colors/descriptions; ensure_compliance_label() best-effort creates the required compliance-audit label.
Complex PR-Based Handlers
scripts/compliance-remediate.sh
remediate_codeowners() generates standard CODEOWNERS, detects existing paths, commits to a branch, and opens a PR; remediate_unpinned_actions() resolves action tags to commit SHAs, rewrites workflow uses: entries, and opens a PR.
Finding Dispatcher & Main
scripts/compliance-remediate.sh
remediate_finding() routes each finding to appropriate handler or records as skipped; main() validates inputs, groups findings by repo, iterates and processes each, writes summary report, and exits non-zero only on unexpected failures.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • #35: Directly implements the automated remediation workflow described in the enhancement: CODEOWNERS generation, label creation, repository setting updates, and unpinned action SHA pinning.
  • #215: The remediation script automates fixes for repository settings, CODEOWNERS, workflow/action SHA pinning, and labels identified in the compliance audit.
  • #160: Implements automated remediation (repo settings, label/CODEOWNERS fixes, and action SHA pinning via PRs or API) that directly addresses the same checks reported in the compliance audit.
  • #173: Directly implements automated fixes for the same remediation categories (repo settings, workflow/action SHA pinning, labels, CODEOWNERS) reported in the compliance audit.

Possibly related PRs

  • petry-projects/.github#12: Adds a remediation script that consumes and acts on the findings.json produced by the audit workflow/script introduced in that PR.
  • petry-projects/.github#79: Both PRs implement automatic creation of missing repository labels using gh label create; the main PR adds remediation script label handling.
  • petry-projects/.github#89: The remediation script auto-fixes unpinned GitHub Actions references and opens PRs for findings that the audit check in that PR is designed to detect.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main addition: a new compliance-remediate.sh script that closes the audit-to-PR remediation loop.
Linked Issues check ✅ Passed The PR implements all core coding objectives from issue #35: direct API remediations for repo settings/labels, PR-based CODEOWNERS generation and action pinning, and documents skipped findings requiring manual handling.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #35 requirements; the script only implements auto-remediation of compliance findings with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/issue-35-20260508-1731

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an org-level remediation script that consumes findings.json from scripts/compliance-audit.sh and attempts to automatically resolve common compliance findings via direct GitHub API changes and PR-based fixes.

Changes:

  • Introduces scripts/compliance-remediate.sh to apply direct remediations (repo settings, labels, check-suite preferences) from audit findings.
  • Adds PR-based remediation flows for CODEOWNERS standardization and GitHub Actions SHA pinning.
  • Generates markdown reports summarizing remediations performed and findings skipped/failed.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +65 to +66
# ---------------------------------------------------------------------------
# Logging helpers
Comment on lines +134 to +153
if [ "$DRY_RUN" = "true" ]; then
skip "[DRY] Would disable check-suite auto-trigger for app $app_id on $ORG/$repo"
report_direct "$repo" "$check" "DRY: would set \`auto_trigger=false\` for app_id=$app_id"
return 0
fi

local payload
payload=$(jq -n --argjson id "$app_id" '{"auto_trigger_checks":[{"app_id":$id,"setting":false}]}')

if echo "$payload" | gh api -X PATCH "repos/$ORG/$repo/check-suites/preferences" \
--input - > /dev/null 2>&1; then
ok "Disabled check-suite auto-trigger for app $app_id on $ORG/$repo"
report_direct "$repo" "$check" "Set \`auto_trigger=false\` for app_id=$app_id"
else
err "Failed to disable check-suite auto-trigger for app $app_id on $ORG/$repo"
report_fail "$repo" "$check" "PATCH check-suites/preferences failed for app_id=$app_id"
fi
}

# ---------------------------------------------------------------------------
Comment on lines +217 to +220
return 0
fi

# Get default branch and its HEAD SHA
Comment on lines +381 to +416
tag_part="${ref##*@}"

# Skip if already a full SHA (shouldn't occur given grep above)
if echo "$tag_part" | grep -qE '^[0-9a-f]{40}$'; then
continue
fi

# Resolve the tag to a commit SHA.
# Lightweight tags point directly to a commit (.object.type == "commit").
# Annotated tags point to a tag object (.object.type == "tag") which
# must be dereferenced to get the commit SHA.
local commit_sha=""
local ref_json
ref_json=$(gh api "repos/$action_part/git/refs/tags/$tag_part" 2>/dev/null || echo "")

if [ -n "$ref_json" ]; then
local obj_type obj_sha
obj_type=$(echo "$ref_json" | jq -r '.object.type // empty')
obj_sha=$(echo "$ref_json" | jq -r '.object.sha // empty')

if [ "$obj_type" = "tag" ]; then
# Annotated tag — dereference the tag object
commit_sha=$(gh api "repos/$action_part/git/tags/$obj_sha" \
--jq '.object.sha' 2>/dev/null || echo "")
else
commit_sha="$obj_sha"
fi
fi

# Fallback: commits API
if [ -z "$commit_sha" ]; then
commit_sha=$(gh api "repos/$action_part/commits?sha=$tag_part&per_page=1" \
--jq '.[0].sha' 2>/dev/null || echo "")
fi

if [ -z "$commit_sha" ]; then
Comment thread scripts/compliance-remediate.sh Outdated
local escaped_ref
escaped_ref=$(printf '%s' "$ref" | sed 's/[[\.*^$()+?{|]/\\&/g')
patched_content=$(echo "$patched_content" \
| sed "s|uses: ${escaped_ref}|uses: ${action_part}@${commit_sha} # ${tag_part}|g")
Comment thread scripts/compliance-remediate.sh Outdated
Comment on lines +264 to +267
encoded_content=$(printf '%s' "$codeowners_content" | base64 -w 0)

local api_args=(-X PUT "repos/$ORG/$repo/contents/$existing_path"
-f message="fix: update CODEOWNERS to use @petry-projects/org-leads team (compliance remediation)"
Comment thread scripts/compliance-remediate.sh Outdated
fi

local new_encoded
new_encoded=$(printf '%s' "$patched_content" | base64 -w 0)
Comment thread scripts/compliance-remediate.sh Outdated
Comment on lines +441 to +445
report_direct "$repo" "unpinned-actions-$workflow_file" \
"DRY: would pin $pins_applied action(s)"
return 0
fi

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/compliance-remediate.sh`:
- Around line 706-713: The current repos assignment silences jq parse/errors via
"|| echo ''" causing malformed FINDINGS_FILE to be treated as no findings;
remove the fallback and instead run jq to populate the repos variable and
immediately check its exit status (i.e., run repos=$(jq -r '[.[].repo] |
unique[]' "$FINDINGS_FILE" 2>/dev/null) and if jq fails — detect non-zero exit
code or empty output due to parse errors — call the script's error/fatal logging
function (e.g., error or fatal) with the jq error context and exit with a
non-zero status; ensure you reference FINDINGS_FILE and the repos variable and
do not swallow jq failures.
- Line 264: The script uses GNU-only base64 flags (e.g., base64 -w 0 and base64
-d) which break on macOS; add portable helper functions b64_encode() and
b64_decode() near the other helpers that detect GNU vs BSD base64 and call the
appropriate options, then replace all raw base64 invocations (e.g., the
encoded_content assignment and the other two occurrences where base64 is used)
to pipe through b64_encode or b64_decode as appropriate so encoding/decoding
works on both Linux and macOS.
- Around line 379-414: The gh API calls use action_part (which may include a
workflow path) instead of the repository slug; extract the repo slug as the
first two path components (e.g.,
repo_slug="${action_part%%/*}/${action_part#*/}" truncated to two components)
before the tag-resolution block and replace action_part with repo_slug in the
three gh api calls that assign ref_json and commit_sha (the calls using
"repos/$action_part/git/refs/tags/$tag_part",
"repos/$action_part/git/tags/$obj_sha", and
"repos/$action_part/commits?sha=$tag_part&per_page=1") so API paths become
"repos/$repo_slug/…". Ensure the new repo_slug variable is used consistently and
falls back to action_part if it cannot be parsed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c46eb8fd-914c-4734-a2d8-a47f99280156

📥 Commits

Reviewing files that changed from the base of the PR and between 73327c7 and b9419cb.

📒 Files selected for processing (1)
  • scripts/compliance-remediate.sh

Comment thread scripts/compliance-remediate.sh Outdated
Comment thread scripts/compliance-remediate.sh Outdated
Comment thread scripts/compliance-remediate.sh
@don-petry don-petry closed this May 11, 2026
@don-petry don-petry reopened this May 11, 2026
@don-petry don-petry closed this May 12, 2026
@don-petry don-petry reopened this May 12, 2026
@don-petry don-petry enabled auto-merge (squash) May 12, 2026 01:42
@github-actions
Copy link
Copy Markdown
Contributor

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved manually.

Please resolve the conflicts and push:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

github-actions Bot and others added 2 commits May 13, 2026 11:54
Adds `scripts/compliance-remediate.sh` to auto-remediate recurring
compliance-audit findings from `findings.json`.

Direct API remediations (applied immediately, no PR):
- `has_wiki=true` → PATCH has_wiki=false
- `allow_auto_merge=false` → PATCH allow_auto_merge=true
- `delete_branch_on_merge=false` → PATCH delete_branch_on_merge=true
- `has_discussions=false` → PATCH has_discussions=true
- `check-suite-auto-trigger-*` → disable for Claude/CodeRabbit app IDs
- `missing-label-*` → gh label create with colors/descriptions from standard

PR-based remediations (creates branch + PR in target repo):
- `missing-codeowners`, `codeowners-empty`, `codeowners-org-leads-not-first`,
  `codeowners-individual-users`, `codeowners-no-catchall` → generates
  `.github/CODEOWNERS` with `* @petry-projects/org-leads` per current standard
- `unpinned-actions-<file.yml>` → resolves tag → commit SHA (handles annotated
  vs. lightweight tags), pins all unpinned refs, opens PR

Skipped with explanation (for human/agent pickup):
- Workflow files, rulesets, dependabot.yml, CLAUDE.md/AGENTS.md,
  CodeQL default setup, push-protection settings

Improvements over prior attempts (claude/issue-35-20260406-0341):
- CODEOWNERS generation uses `@petry-projects/org-leads` team (not individual
  users — forbidden per codeowners-standard.md updated 2026-05-04)
- Handles all five CODEOWNERS finding variants, not just `missing-codeowners`
- Adds `in-progress` label to the label map (was missing)
- Adds check-suite auto-trigger remediation (new audit finding)
- Bash 4+ version guard (consistent with apply-repo-settings.sh)

Closes #35

Co-authored-by: Don Petry <don-petry@users.noreply.github.com>
- SC2034: Remove unused CHECK_SUITE_APP_IDS array (app_id extracted from
  finding check name at runtime, no static list needed)
- SC2155: Split local declarations from command-substitution assignments
  (local branch_name; branch_name="...$(date ...)")
- SC2221/SC2222: Move ci-workflows/missing-permissions-* before the more
  general ci-workflows/missing-* to prevent pattern override

Co-authored-by: Don Petry <don-petry@users.noreply.github.com>
@don-petry don-petry force-pushed the claude/issue-35-20260508-1731 branch from 8a49089 to ae0f4c8 Compare May 13, 2026 16:54
This was referenced May 13, 2026
@donpetry-bot
Copy link
Copy Markdown
Contributor

donpetry-bot commented May 13, 2026

Superseded by automated re-review at ce6f67810b3075050c46f4ee9a8def20aa27633b — click to expand prior review.

Review — fix requested (cycle 1/3)

The automated review identified the following issues. Please address each one:

Findings to fix

[Findings would be inserted here]

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

@donpetry-bot
Copy link
Copy Markdown
Contributor

donpetry-bot commented May 14, 2026

Superseded by automated re-review at 4b39732263d7422ff9daf2242ff2d9d6c9044656 — click to expand prior review.

Review — fix requested (cycle 2/3)

The automated review identified the following issues. Please address each one:

Findings to fix

[Findings would be inserted here]

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

@don-petry
Copy link
Copy Markdown
Contributor Author

@coderabbitai resolve

@don-petry
Copy link
Copy Markdown
Contributor Author

Dev-Lead — review-changes (applied)

Changes committed and pushed.

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 24, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 24, 2026

✅ Actions performed

Comments resolved and changes approved.

@don-petry
Copy link
Copy Markdown
Contributor Author

Dev-Lead — fix-reviews (applied)

Changes committed and pushed.

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

enhancement: Automate remediation of recurring compliance-audit findings

3 participants