ci: scan issue and comment bodies — this repo has never scanned one - #43
ci: scan issue and comment bodies — this repo has never scanned one#43yakimoto wants to merge 13 commits into
Conversation
…ment body Measured across all 28 public wave-av repos (claude-workstation#1747, #1794): TWO coverage shapes satisfy the one required check name `Secrets + content policy`. 27 repos triggers: pull_request, push, workflow_dispatch jobs: guard 1 repo triggers: + issues, issue_comment jobs: + body-guard This repo is in the 27. All 28 report the same green check. The outlier is wave-moq-edge, and its own comment says why it matters: "`edited` matters as much as `opened`: a body can be made to leak long after the PR is first raised, and until this workflow covered it, nothing ever re-scanned." A PR/issue/comment BODY is exactly as world-readable as the tree, and until now it was scanned by nothing server-side. That gap was not theoretical on wave-moq-edge: a PR was blocked for naming a private repo in wrangler.toml while the very same name, with more operational detail attached, sat unchallenged in its body. WHAT LANDS HERE — the bundle the workflow's own header names, minus what this repo already has (.gitleaks.toml and content-policy.sh are already vendored): .github/workflows/public-repo-guard.yml replaced (73 -> 163 lines) scripts/public-repo-guard/body-policy.sh new, mode 100755 scripts/public-repo-guard/tests/body-policy.test.sh new, mode 100755 Copied from wave-moq-edge, which has run this shape in production. Modes preserved via the git trees API — the contents API would have created both scripts 100644. HONEST ABOUT WHAT IT CAN DO. On a PR this PREVENTS the merge. On an issue or comment the text is already public the moment it posts, so this is DETECTION: it says go redact, fast. Only a client-side pre-write hook stops that class before publication. Also inherited from the reference: concurrency moves from workflow-level to PER JOB, because the two jobs want opposite behaviour. A workflow-level group forced one policy on both, and rapid body edits cancelled the tree job repeatedly — every cancelled check-run stays attached to the commit, so the PR reported UNSTABLE while the live runs were green. The body gate ships with its own fixtures and runs them in CI. Its NEGATIVE cases are the load-bearing half: a leak gate that blocks legitimate cross-repo references gets switched off, and then it protects nothing. Refs wave-av/claude-workstation#1747. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_c47dab26-9abf-4f10-9a58-a27ba576d46b) |
|
Important Review skippedAuto reviews are limited based on label configuration. 🚫 Excluded labels (none allowed) (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
ApprovabilityVerdict: Needs human review Unable to check for correctness in 75f4f7a. This PR adds a new security scanning job that gates PR merges by scanning titles/bodies for sensitive content (~300 lines of new scripts). While the author owns all changed files, the introduction of new merge-blocking workflow logic and significant new scanning rules warrants human review to verify the approach and behavior. You can customize Macroscope's approvability policy. Learn more. |
|
Running ultrareview automatically — This adds a new CI security gate scanning untrusted PR/issue/comment bodies with regex-based policy and fail-closed paths, plus workflow event/concurrency changes — a subtle regex or workflow edge case could silently skip scans, leak internal names, or break merge gating.. I'll post findings when complete. |
PR Summary by QodoCI: scan PR/issue/comment bodies with public-repo-guard
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTo customize comments, go to the Qodo configuration screen, or learn more in the docs. |
Qodo FixerNo findings are available for this PR yet. Findings appear here once Qodo has reviewed the PR. |
…aundering Address review findings: - body-guard now checks out the gate scripts from the base branch tip (pull_request.base.sha), not the PR merge ref, so a fork PR can no longer rewrite body-policy.sh to pass its own body. - The ABOUT_THE_CONTROL allowlist no longer applies to credential-format rules: a line naming the gate can still carry a live key. Only the explicit guard:allow marker exempts those. Fixture added. - Preflight the ripgrep build for PCRE2 (script + both install steps) so a non-PCRE2 rg fails with the real cause instead of an opaque scanner error. Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
…dentifiers Address review findings: - The private-repo-ops pattern opens with a global (?i) so repo names match case-insensitively, but that flag reached OPS_DETAIL and defeated its SCREAMING_CASE requirement: lowercase identifiers like api_key next to a repo name blocked clean PRs. The credential-name branch is now (?-i:...). - Infra identifiers (Cloudflare account_id, Tailscale IP, operator path) now ignore the ABOUT_THE_CONTROL allowlist like the credential formats do: a real internal IP on a line naming the gate is still a leak. - Fixtures for both. Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
…s, not 4) Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
The trusted-ref checkout means the installing PR's base has no body-policy.sh yet. That is exactly the pre-install state (no gate), and a PR head cannot cause it, so skip with a visible warning instead of exiting 127; the gate is live from the first event after merge. Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
…s off - issue_comment runs now scan just the new comment body. Re-scanning the parent issue's title/body meant one old violation failed every future comment, with line numbers into a concatenated buffer that maps to nothing. Each text is judged exactly once, on the event that changes it. - An empty GUARD_PRIVATE_REPOS (fork runs may not receive org variables) now emits a visible warning instead of a quietly green check with the highest-value rule off. Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
…banners Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
…ures Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f411b32c-4210-4040-8d71-4f8e64ccb48b) |
| env: | ||
| RIPGREP_VERSION: "14.1.1" | ||
| RIPGREP_SHA256: "4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e" |
There was a problem hiding this comment.
🔍 Pinned ripgrep checksum is unverifiable from the repo
The fallback install pins ripgrep 14.1.1 with SHA-256 4cf9f2...; unlike the gitleaks pin this value appears nowhere else in the repo, so a typo would only surface as a hard failure of both jobs at the moment the preinstalled runner rg loses PCRE2 — i.e. precisely the fallback path this step exists to keep working, and one that never executes on today's runner images. Worth verifying the digest against the upstream release once, since the failure mode is silent until it is urgent.
Was this helpful? React with 👍 or 👎 to provide feedback.
… the required check Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
… the body gate Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_9a3fb931-0615-4088-a68b-9c131ab05f3e) |
This repo's
public-repo-guardhas never scanned a single issue or comment body.Measured across all 28 public wave-av repos (
wave-av/claude-workstation#1747,#1794): two coverage shapes satisfy the one required check nameSecrets + content policy.pull_request, push, workflow_dispatchguardissues,issue_commentbody-guardThis repo is in the 27. All 28 report the same green check — because a required check asserts that something named X passed, never what X examined.
The outlier is
wave-moq-edge, and its own comment says why it matters:That gap was not theoretical there: a PR was blocked for naming a private repo in
wrangler.tomlwhile the very same name, with more operational detail attached, sat unchallenged in its body.What lands
Three files — the bundle the workflow's own header names, minus what this repo already has (
.gitleaks.tomlandcontent-policy.share already vendored, and are checked as prerequisites; a repo missing either is refused rather than half-installed):The workflow's header names four files as the install unit but executes a fifth —
tests/body-policy.test.sh, in its own self-test step. Omitting it installs a workflow that fails on a step nobody read, so the manifest ships it. Modes are preserved via the git trees API; the contents API creates100644regardless, which would silently break running these scripts as executables.Planned by
governance/lib/vendor-bundle.mjs(claude-workstation#1850) against a checked-in manifest, not by ad-hoc shell.One deliberate divergence from the reference, stated rather than silent
The shipped workflow is
wave-moq-edge's withactions/checkoutbumped from v5.0.1 to v7.0.1 (3d3c42e5aac5ba805825da76410c181273ba90b1), the pin already used byclaude-workstation's own gate.Copying verbatim was checked first and rejected on evidence: of the 18 target repos, 17 carry a byte-identical guard, and
wave-realtime-edgealready runs v7.0.0 — so a verbatim copy would have downgraded it, and shipped a stale pin to the other 17. A separate PR brings the reference itself up to the same pin.Honest about what this can and cannot do
On a PR this PREVENTS the merge. On an issue or comment the text is already public the moment it posts, so this is DETECTION: it says go redact, fast. Only a client-side pre-write hook stops that class before publication.
Also inherited from the reference: concurrency moves from workflow-level to per job, because the two jobs want opposite behaviour. A workflow-level group forced one policy on both, and rapid body edits cancelled the tree job repeatedly — every cancelled check-run stays attached to the commit, so the PR reported UNSTABLE while the live runs were green.
The body gate ships with its own fixtures and runs them in CI. Its negative cases are the load-bearing half: a leak gate that blocks legitimate cross-repo references gets switched off, and then it protects nothing.
Refs
wave-av/claude-workstation#1747.Note
Medium Risk
Changes merge-gate and branch-protection behavior (required checks, concurrency, bootstrap skip) and expand what content can block merges; logic is security-sensitive but heavily documented and covered by fixtures.
Overview
Extends public-repo-guard so PR, issue, and comment titles/bodies are scanned server-side, not only the published tree. A new
body-guardjob runs onpull_request,issues, andissue_comment(includingedited), materializes event text to a temp file viajq, and runsbody-policy.shfrom a trusted base ref (sparse checkout) so fork PRs cannot substitute a no-op scanner.The existing
guardjob still scans the tree with gitleaks andcontent-policy.sh, but now also installs a pinned, checksum-verified ripgrep with PCRE2, runsbody-policy.test.shin CI, uses per-job concurrency (tree cancels in progress; body does not), and still runs oneditedPRs so a skipped required check cannot mask a failed tree scan.body-policy.shmirrors many tree rules (credentials, CFaccount_id, internal IPs, operator paths, internal markers) with redacted annotations,guard:allow, and an “about the control” allowlist—but private repo names only block when paired with operational detail within ~140 characters (unlikecontent-policy.sh, which blocks bare names in files).GUARD_PRIVATE_REPOSstill drives the name list from org vars.tests/body-policy.test.shadds fixture positives/negatives (including bare cross-repo references) and fail-closed paths;actions/checkoutis bumped to v7.0.1 in the workflow.Reviewed by Cursor Bugbot for commit 75f4f7a. Configure here.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is enabled.Note
Scan PR, issue, and comment bodies for credential and operational leaks in public-repo-guard
Body content policyjob to public-repo-guard.yml triggered on PR edits, issue opens/edits, and comment creates/edits; the job materializes body text from the event payload and runs the policy script.guard:allowinline markers exempt specific lines with a required reason.Macroscope summarized 75f4f7a.