Skip to content

feat(standards): code scanning merge protection on every default branch (CodeQL, medium_or_higher) - #1358

Merged
don-petry merged 4 commits into
mainfrom
feat/code-scanning-merge-protection
Oct 20, 2026
Merged

don-petry merged 4 commits into
mainfrom
feat/code-scanning-merge-protection

Conversation

@don-petry

@don-petry don-petry commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What

Makes code scanning merge protection on every default branch an org standard: a code_scanning ruleset rule for CodeQL with security_alerts_threshold: medium_or_higher and alerts_threshold: errors.

Why: the required CodeQL status check only says the analysis ran. GitHub's code scanning check fails only on high or critical security alerts, so a pull request that adds a medium security alert merges silently. This rule blocks that merge.

Part Change
Doc standards/github-settings.md § Code scanning merge protection (All Repositories): the values, why, the floor rule, what feeds it, what a repo with no CodeQL language does, and GitHub's limits. Pointers added in ci-standards.md §2, advanced-security.md (audit check 5), standards/rulesets/README.md and AGENTS.md. The AGENTS.md note this replaces said google-app-scripts had a protect-branches ruleset with code scanning. It doesn't any more (live, it has only code-quality and pr-quality), so that note was out of date.
Ruleset data standards/rulesets/code-quality.json gains the rule beside its required status checks, so apply-rulesets.sh applies it along with the rest of code-quality.
Never lowered New scripts/lib/code-scanning.sh holds the one ordering of GitHub's threshold values (none < critical < high_or_higher < medium_or_higher < all; none < errors < errors_and_warnings < all). Before a PUT, apply-rulesets.sh reads the live ruleset and keeps any stricter threshold. If it cannot read the live ruleset it does not PUT, so it can never lower a stricter setting. --dry-run reports when it would keep one.
Sweep compliance-audit.sh gets check_code_scanning_rule, called from check_rulesets. It reads GET repos/{o}/{r}/rules/branches/{default}, which lists the rules from every active ruleset, and compares the strictest CodeQL entry with the floor in code-quality.json. Findings: code-scanning-rule-missing, code-scanning-rule-weak-security_alerts_threshold, code-scanning-rule-weak-alerts_threshold, plus fail-closed -unreadable (the repo is also marked inconclusive) and -source-invalid. All of them use category rulesets, so they route to dev-lead:hands-off like every other ruleset finding. A stricter repo (all) passes.
Tests test/scripts/compliance-audit/code-scanning-rule.bats (19 tests): at the floor, stricter, the rule living in the repo's own ruleset, layered rulesets, pagination, missing, another tool only, each weaker field, an unknown value, unreadable, not-an-array, source invalid, wiring and routing. test/scripts/apply-rulesets/apply-rulesets.bats gains 9 tests: the codified rule, create, keeping a stricter live value for each field, raising a weaker one, no live rule, unreadable live means no PUT, dry-run, and the pure payload function. apply-rulesets-tests.yml now also triggers on scripts/lib/code-scanning.sh.

Nothing here writes any live setting. The rule reaches a repo only when the audit or the applier next runs after merge.

What the next runs will change (read live on 2026-10-20, without writing)

Repo Effective rule on main today Next audit (Fri 12:57 UTC) Next apply-repo-settings self-heal (Mon 06:17 UTC)
software-factory CodeQL all / errors (own main ruleset) passes, no finding not reached (no stub)
.github none code-scanning-rule-missing adds the rule to code-quality
empower-personal-dashboard none code-scanning-rule-missing adds the rule
markets none code-scanning-rule-missing adds the rule
bmad-bgreat-suite none code-scanning-rule-missing adds the rule
TalkTerm none code-scanning-rule-missing adds the rule
.github-private, incubator, repo-template, ContentTwin, broodly, google-app-scripts, broodminder-data none code-scanning-rule-missing not reached (no apply-repo-settings.yml stub): needs apply-rulesets.sh --repo combsmith/<repo> code-quality with an admin token, per the runbook
software-factory-canary none (own main ruleset, no code-quality) code-scanning-rule-missing, next to the ruleset findings it already gets not reached

So the audit files 13 new Compliance: code-scanning-rule-missing issues, one per repo, and 0 weak-threshold issues: no repo has a weaker rule today. The self-heal then fixes 5 of them. Every repo has CodeQL working, so the rule won't block merges because a tool is missing: 12 use default setup configured, and .github and .github-private run the advanced codeql.yml. Every repo reports at least the actions language.

combsmith/software-factory and #1352

software-factory asked in combsmith/.github#1352 to be left out of the compliance audit, the scorecard and the fleet monitor. That issue is still open, and the audit has no opt-out mechanism yet. This PR doesn't add one and doesn't depend on one:

Merge queues: verified against GitHub's docs

GitHub's merge protection page says merge protection with rulesets does not apply to "Merge queue groups", or to "Dependabot pull requests analyzed by default setup". It is "not related to status checks". The rules reference says it blocks a merge when a required tool finds an alert at the configured severity, when its analysis is still in progress, or when it "is not configured for the repository".

So in the repos with a merge queue (.github-private, software-factory), the rule is checked on the pull request, not on the merge_group run. The group is gated only by required status checks, which for CodeQL means the advanced codeql.yml's CodeQL check. Not verified: whether GitHub checks the rule when a pull request is added to the queue, or only on a direct merge. The docs don't say, and I ran no trial. The standard says this.

Checks run locally

  • shellcheck --severity=warning -x scripts/*.sh scripts/lib/*.sh: clean
  • bats test/scripts/apply-rulesets/ test/scripts/compliance-audit/: all pass (bash 5)
  • markdownlint-cli2 "**/*.md": 0 errors
  • test/scripts/apply-repo-settings/reusable-contract.bats fails locally only because yq isn't installed here. It's unrelated and doesn't touch these files.

🤖 Generated with Claude Code

View guided diff Turn on auto-fix

Make "a code-scanning rule on every repo" an org standard:

- standards/rulesets/code-quality.json gains a code_scanning rule: CodeQL,
  security_alerts_threshold medium_or_higher, alerts_threshold errors.
  GitHub's code scanning check fails only on high/critical by default, so a
  medium security alert merged silently.
- The thresholds are a floor. scripts/lib/code-scanning.sh holds the one
  ordering of GitHub's threshold values; apply-rulesets.sh keeps a stricter
  live threshold on update (and does not PUT when it cannot read the live
  ruleset), so a repo at `all` is never lowered.
- compliance-audit.sh: check_code_scanning_rule reads the rules that apply to
  the default branch (any active ruleset) and reports
  code-scanning-rule-missing / -weak-<field>, failing closed on an unreadable
  read. Stricter passes. Category rulesets, so findings route to
  dev-lead:hands-off like every ruleset finding.
- Documented in github-settings.md (why, the floor, what feeds it, a repo
  with no CodeQL language, merge-queue and Dependabot limits), with pointers
  from ci-standards.md, advanced-security.md, the rulesets README and AGENTS.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

This comment has been minimized.

@don-petry
don-petry requested a review from a team as a code owner October 20, 2026 17:20
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: ea6dbf66-176c-45ff-a07d-6748b49510ea
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — waiting on PR blockers (intent: review-changes)

PR: #1358
No changes were committed, but the PR still can't be marked done: required check CodeQL is failing. The retry cron will re-attempt automatically. Next attempt after: 2026-10-20T18:18:19Z

@don-petry

Copy link
Copy Markdown
Contributor Author

Note

@don-petry I reviewed this PR and no code changes were needed, but I can't mark it done yet: required check CodeQL is failing. I'll re-check automatically.
Next attempt after: 2026-10-20T18:18:19Z

@don-petry
don-petry enabled auto-merge (squash) October 20, 2026 17:48
@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-10-20T20:48:40Z.

@donpetry-bot

donpetry-bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

No description provided.

@don-petry
don-petry disabled auto-merge October 20, 2026 21:04
@don-petry

Copy link
Copy Markdown
Contributor Author

No actionable findings identified. The code review summary indicates a status of "Failed" but provides no specific defects tied to files or line numbers. All CI checks are passing and there are no review threads with detailed issues to address.

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — fix-bot-comment (no-changes)

Agent reasoning
## Summary
**Bot:** chatgpt-codex-connector[bot]
**Issues addressed:** 0 actionable findings
**Action taken:**
- Code review status summary (no detailed findings): dispositioned as `informational` with comment ID `IC_kwDORyesfc8AAAABd1IUpg`
**Files changed:** None
**Skipped (informational):** 1
---
The bot posted a summary status indicating a "Failed" code review with `has_any_findings:true`, but the comment contains no specific, actionable defects — no file/line references, no detailed issues list. There are no review threads from this bot on the PR, and all CI checks are passing. This is a non-actionable notice (a status summary without concrete findings), so it has been dispositioned as `informational` to clear the maintainer-comment gate.

@don-petry
don-petry enabled auto-merge (squash) October 20, 2026 21:06
@don-petry

Copy link
Copy Markdown
Contributor Author

@donpetry-bot review — CI is green and the advisory bots are rate-limited (Codex out of usage, CodeRabbit limited); please review this PR.

@donpetry-bot

Copy link
Copy Markdown
Contributor

@don-petry I'm on it — starting a fresh review now. Results will appear in a few minutes.

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: MEDIUM
Reviewed commit: fb98442eb650fe13b8061729cd57d715b908df19
Review mode: triage-approved (single reviewer)

Summary

Adds a CodeQL code_scanning ruleset rule (security_alerts_threshold medium_or_higher, alerts_threshold errors) to the codified code-quality ruleset as an org standard. It also adds a shared threshold-ordering library so apply-rulesets.sh never lowers a stricter live threshold and compliance-audit.sh reports a missing or weaker rule (fail-closed). Tests and docs are included. The triage low-risk call holds up. I rated it MEDIUM rather than LOW because it is non-trivial logic in fleet-wide enforcement tooling, but it only tightens security and touches no secrets.

Linked issue analysis

No closing issue is linked. #1352 is referenced for context only and is correctly not closed. The PR body explains the motivation, the expected effect on each repo (13 code-scanning-rule-missing findings, 5 self-healed) and the merge-queue limitation it did not verify.

Findings

No blocking findings.

Verified:

  • Floor logic (scripts/lib/code-scanning.sh). I sourced the library and exercised it directly. With no rule it reports missing. A stricter all passes. With layered weaker rulesets it takes the strictest entry per field and reports the shortfall correctly. cs_floor_payload keeps a live all and raises a live none to the floor. An empty live ruleset gives back the codified file. Unknown or null values rank -1, so the audit fails closed and the applier never keeps them.
  • Applier. It does no PUT when the live ruleset can't be read, and the temp payload is cleaned up on every path. The create (POST) path is unchanged and already carries the rule.
  • Audit. It reads rules/branches/{default} across all active rulesets. pipefail is set, so a gh failure becomes -unreadable and the repo is marked inconclusive. The existing check_ruleset_contents drift check compares only required_status_checks for code-quality, so a stricter live code_scanning value won't show up as drift there.
  • shellcheck --severity=warning -x is clean on all three scripts. bats wasn't available in this runner, but the CI bats / Lint and bats jobs pass.
  • No secrets or internal-only details in the diff (public repo). The MCP secret scan tool isn't available in this run; gitleaks CI passed.

Non-blocking notes:

  • cs_floor_payload rewrites only the codified tool(s). A code_scanning entry for another tool in a repo's live code-quality ruleset would be dropped on PUT. That matches the existing full-replace convergence semantics, but it is worth knowing.
  • The PR body lists the audit finding slugs but leaves out code-scanning-rule-uncomparable, which the code also emits.
  • Rollout: the rule blocks merges while CodeQL is still running or isn't configured. The PR body says every repo has CodeQL configured; it's worth watching the first self-heal run.

CI status

All required checks are green: CodeQL, Analyze (actions/python), SonarCloud, ShellCheck, bats, Lint and bats, gitleaks, AgentShield, dependency-audit. Skipped jobs aren't relevant. There are no unresolved review threads and no unanswered human questions. Codex and CodeRabbit were rate-limited and gave no substantive findings. mergeStateStatus is BEHIND, which isn't blocking.


Reviewed automatically by the PR-review agent (single-reviewer mode: opus 5.5 [opus 4.8, opus 4.7]). Reply if you need a human review.

@donpetry-bot

Copy link
Copy Markdown
Contributor

pr-review approved on PARTIAL advisory evidence: 2/5 required advisory bots reported before the gate's head-age-timeout fallback proceeded. Recorded for the miss-rate metric (#1596).

@sonarqubecloud

Copy link
Copy Markdown

@don-petry
don-petry disabled auto-merge October 20, 2026 21:27
@don-petry
don-petry merged commit c659ebe into main Oct 20, 2026
33 checks passed
@don-petry
don-petry deleted the feat/code-scanning-merge-protection branch October 20, 2026 21:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants