Skip to content

ci: scan issue and comment bodies — this repo has never scanned one - #22

Open
yakimoto wants to merge 8 commits into
mainfrom
ci/1747-public-repo-guard-body-scan
Open

ci: scan issue and comment bodies — this repo has never scanned one#22
yakimoto wants to merge 8 commits into
mainfrom
ci/1747-public-repo-guard-body-scan

Conversation

@yakimoto

@yakimoto yakimoto commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

This repo's public-repo-guard has 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 name Secrets + content policy.

repos triggers jobs
27 pull_request, push, workflow_dispatch guard
1 + issues, issue_comment + body-guard

This 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:

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.

That gap was not theoretical there: 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

Three files — the bundle the workflow's own header names, minus what this repo already has (.gitleaks.toml and content-policy.sh are already vendored, and are checked as prerequisites; a repo missing either is refused rather than half-installed):

.github/workflows/public-repo-guard.yml               replaced (72 -> 163 lines)
scripts/public-repo-guard/body-policy.sh              new, mode 100755
scripts/public-repo-guard/tests/body-policy.test.sh   new, mode 100755

The workflow's header names four files as the install unit but executes a fifthtests/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 creates 100644 regardless, 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 with actions/checkout bumped from v5.0.1 to v7.0.1 (3d3c42e5aac5ba805825da76410c181273ba90b1), the pin already used by claude-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-edge already 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.


View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.


Note

Cursor Bugbot is generating a summary for commit 6cb1806. Configure here.

Note

Add CI scanning of PR/issue/comment bodies for internal-leak patterns

  • Adds a new body-guard job in public-repo-guard.yml that triggers on issue, comment, and review events and scans their text bodies.
  • Adds body-policy.sh, which uses ripgrep (PCRE2) to detect credential formats and infrastructure identifiers, with allowlisting via guard:allow markers and redacted annotations.
  • The scanner fails closed on missing prerequisites or ripgrep errors; exit codes are 0 (clean), 1 (blocking violation), or 2 (scanner error).
  • Adds a test harness in body-policy.test.sh that the guard job runs as a self-test step on every CI run.
  • The existing guard job gains per-PR concurrency with cancel-in-progress and skips body-only events to avoid redundant tree scans.

Macroscope summarized 7ed1c87.

Review in cubic


Open in Devin Review

…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>
@yakimoto yakimoto added the rr:skip-coderabbit RF.P1 reviewer routing (#1039) label Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🚫 Excluded labels (none allowed) (1)
  • rr:skip-coderabbit

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 85543a91-9011-4183-9ffc-bca1e7fc5808

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@cursor

cursor Bot commented Aug 6, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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_564263c8-fbd8-45fb-a052-aef81703fe2d)

@macroscopeapp

macroscopeapp Bot commented Aug 6, 2026

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

Unable to check for correctness in 7ed1c87. New security scanning feature with ~345 lines of new scripts. Multiple unresolved review comments identify potential bypass paths (fork PRs supplying their own gate script, silent rule skipping). Security-related CI changes with substantive open concerns warrant human review.

You can customize Macroscope's approvability policy. Learn more.

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 6, 2026
@cubic-dev-ai

cubic-dev-ai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Running ultrareview automatically — Adds a security gate over untrusted PR/issue/comment bodies with hand-rolled regex/allowlist logic; a missed bypass can leak internal details to a public repo, and a false positive can block every PR, so this CI change deserves multi-pass review.. I'll post findings when complete.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

CI: scan PR/issue/comment bodies with public-repo-guard

✨ Enhancement ⚙️ Configuration changes 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Add issue/comment body scanning alongside existing tree-based secret/policy checks.
• Split concurrency per job to avoid canceling tree scans during rapid body edits.
• Introduce body-policy script and fixtures to fail closed without leaking matches.
Diagram

graph TD
  A["GitHub events"] --> B["public-repo-guard.yml"]
  B --> C["guard job"] --> D["content-policy.sh (tree)"]
  C --> E["body-policy fixtures"]
  B --> F["body-guard job"] --> G["jq -> body.txt"] --> H["body-policy.sh"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Rely on GitHub secret scanning + push protection
  • ➕ Native platform capability with low maintenance
  • ➕ Detects many credential formats automatically
  • ➖ Does not cover the repo’s custom “content policy” rules (internal markers, private-repo+ops-detail proximity)
  • ➖ May not scan/comment on all the same text surfaces in the same way or with the same required-check semantics
2. Use gitleaks GitHub Action for body text
  • ➕ Single vendor tool for both tree and body scanning
  • ➕ Less custom regex/policy code to maintain
  • ➖ The gitleaks Action requires paid licensing for organizations (per PR description)
  • ➖ Still requires careful handling to avoid echoing leaked text in logs/annotations
3. Use pull_request_target for body scanning with elevated permissions
  • ➕ Simplifies access patterns for some PR metadata and could avoid some checkout work
  • ➖ Security risk: grants write token/secrets context to untrusted fork content
  • ➖ Contradicts the goal of never elevating privileges just to read body text

Recommendation: Keep the PR’s current approach: a separate, minimal-permissions body-guard job that reads bodies from the event payload, materializes them to a file, and scans with a dedicated policy script that redacts matches. This preserves security boundaries (no pull_request_target), avoids paid tooling, and addresses the real gap (server-side body scanning) while keeping false positives controllable via proximity rules and explicit allow markers.

Files changed (3) +342 / -5

Enhancement (1) +139 / -0
body-policy.shIntroduce body text content policy scanner with redaction and fail-closed behavior +139/-0

Introduce body text content policy scanner with redaction and fail-closed behavior

• Adds a new ripgrep-based scanner for PR/issue/comment text, with allowlisting (guard:allow and “about the control” patterns) to reduce false positives. Implements rules for credential formats, infra identifiers, and internal-only markers, plus a proximity-based private-repo+operational-detail check driven by GUARD_PRIVATE_REPOS. Ensures violations annotate only line numbers (not matched text) and fails closed on scanner errors.

scripts/public-repo-guard/body-policy.sh

Tests (1) +108 / -0
body-policy.test.shAdd fixture-only tests for body-policy precision and redaction guarantees +108/-0

Add fixture-only tests for body-policy precision and redaction guarantees

• Introduces hermetic fixtures validating both blocking cases and critical non-blocking cases (deployability/precision), including quoting rules for use-vs-mention and guard:allow behavior. Verifies the scanner never echoes matched text into annotations and includes fail-closed tests for missing/invalid inputs.

scripts/public-repo-guard/tests/body-policy.test.sh

Other (1) +95 / -5
public-repo-guard.ymlAdd body-guard job and issue/comment triggers; split concurrency per job +95/-5

Add body-guard job and issue/comment triggers; split concurrency per job

• Extends workflow triggers to include issues and issue_comment (including edited events) and adds a new Body content policy job. Moves concurrency to the job level to prevent body-edit churn from canceling tree scans, and bumps actions/checkout to a v7.0.1 SHA pin. Adds a fixture self-test step so body-policy regressions fail CI early.

.github/workflows/public-repo-guard.yml

@qodo-code-review

qodo-code-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Install list missing test ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The workflow header says installing public-repo-guard requires copying four files, but the
workflow unconditionally runs scripts/public-repo-guard/tests/body-policy.test.sh. Following the
documented install list in another repo would break CI at the self-test step.
Code

.github/workflows/public-repo-guard.yml[R16-19]

+# To install on a new repo, copy all four files together:
#   .github/workflows/public-repo-guard.yml
#   .gitleaks.toml
#   scripts/public-repo-guard/content-policy.sh
Evidence
The workflow’s install instructions list only four files, but the guard job invokes the
body-policy fixture test script directly, making it a required part of any installation that follows
the header.

.github/workflows/public-repo-guard.yml[16-20]
.github/workflows/public-repo-guard.yml[96-101]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The workflow’s “install on a new repo” comment lists only 4 files, but the workflow *executes* a 5th file (`scripts/public-repo-guard/tests/body-policy.test.sh`) during CI. Downstream repos that follow the documented instructions will copy an incomplete set and get a failing workflow.

### Issue Context
The `guard` job runs `body policy self-test (fixtures)` and calls the test script directly.

### Fix Focus Areas
- .github/workflows/public-repo-guard.yml[16-20]
- .github/workflows/public-repo-guard.yml[96-101]

### Expected fix
Update the install header to include:
- `scripts/public-repo-guard/tests/body-policy.test.sh`

(Alternative: if you truly want a 4-file install unit, gate the self-test step behind a presence check or move tests out of the workflow.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
Review mode: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 5/18, lines 347/200; both must reach the floor). Router rationale: This adds security-sensitive CI behavior plus substantial independent workflow, event-payload, regex-scanner, and fixture-test logic, creating multiple easy-to-miss failure and bypass modes.

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

qodo-code-review[bot]

This comment was marked as resolved.

@qodo-code-review

Copy link
Copy Markdown

Qodo Fixer

No findings are within the configured fix scope. To change which findings are fixed, adjust the setting on your Qodo configuration page.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 6, 2026

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 6 potential issues.

Open in Devin Review

Comment thread scripts/public-repo-guard/body-policy.sh
Comment thread scripts/public-repo-guard/body-policy.sh
Comment thread .github/workflows/public-repo-guard.yml
Comment on lines +129 to +134
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Only the gate's own scripts are needed — no reason to pay for the whole
# tree on every comment.
sparse-checkout: scripts/public-repo-guard
sparse-checkout-cone-mode: false

@devin-ai-integration devin-ai-integration Bot Aug 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Fork PRs supply the gate script that judges them, and vars may be unavailable

body-guard runs on pull_request and checks out the PR's own copy of scripts/public-repo-guard, so a fork PR can modify body-policy.sh in the same PR and the gate that evaluates its body is the modified one. This mirrors the pre-existing arrangement for content-policy.sh in the guard job, so it is not a regression, but it means the body gate is advisory against a motivated author. Separately, vars.GUARD_PRIVATE_REPOS (.github/workflows/public-repo-guard.yml:163) may not be exposed to fork-originated pull_request runs; if it is empty the private-repo-ops check silently skips (scripts/public-repo-guard/body-policy.sh:117) while the job still reports a pass.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread .github/workflows/public-repo-guard.yml
Comment thread scripts/public-repo-guard/body-policy.sh Outdated
…al formats

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ultrareview completed in 10m 12s

3 issues found and verified against the latest diff

Confidence score: 2/5

  • In .github/workflows/public-repo-guard.yml, the body-content check runs scripts/public-repo-guard/body-policy.sh from the PR’s own checkout, so an attacker can alter the scanner and bypass the guard on untrusted PR text; this undercuts the core protection intent — run the policy script from a trusted ref (or inline/pinned action code) instead of PR-controlled files.
  • In .github/workflows/public-repo-guard.yml, skipping all pull_request.edited events creates a validation gap where non-body/title edits can avoid tree scanning, which can let risky metadata changes slip through undetected — narrow the skip logic to only safe text-only edits or re-enable scans for edited events.
  • In .github/workflows/public-repo-guard.yml, setup docs mention copying four files while the workflow requires scripts/public-repo-guard/tests/body-policy.test.sh, so new adopters can get failing required checks from incomplete installation — align the install instructions with all required files (or make the self-test conditional).
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/public-repo-guard.yml">

<violation number="1" location=".github/workflows/public-repo-guard.yml:20">
P2: The install header says to copy four files to set up public-repo-guard in a new repo, but the guard job unconditionally executes a fifth file, scripts/public-repo-guard/tests/body-policy.test.sh, as a required self-test step. A repo that follows the documented four-file install list will fail CI at this step since the test script won't exist. Update the header to include the test script (or gate/remove the self-test step for minimal installs).</violation>

<violation number="2" location=".github/workflows/public-repo-guard.yml:58">
P2: The tree scan now skips every `pull_request.edited` event, which creates a blind spot for edited actions that are not just body/title text (for example PR metadata changes that can affect what should be validated). Running `guard` for all PR actions avoids relying on `edited` being text-only and keeps the scan behavior fail-closed.</violation>

<violation number="3" location=".github/workflows/public-repo-guard.yml:132">
P1: The body-content gate can be bypassed on pull requests because it executes `scripts/public-repo-guard/body-policy.sh` from the PR checkout. Since this job is meant to validate untrusted PR text, running the scanner from the same untrusted ref allows a PR to weaken or disable the scan and still produce a green check. Using a trusted ref for the scanner (for PRs, the base SHA) keeps the policy enforcement tamper-resistant.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread scripts/public-repo-guard/body-policy.sh
with:
# Only the gate's own scripts are needed — no reason to pay for the whole
# tree on every comment.
sparse-checkout: scripts/public-repo-guard

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: The body-content gate can be bypassed on pull requests because it executes scripts/public-repo-guard/body-policy.sh from the PR checkout. Since this job is meant to validate untrusted PR text, running the scanner from the same untrusted ref allows a PR to weaken or disable the scan and still produce a green check. Using a trusted ref for the scanner (for PRs, the base SHA) keeps the policy enforcement tamper-resistant.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/public-repo-guard.yml, line 132:

<comment>The body-content gate can be bypassed on pull requests because it executes `scripts/public-repo-guard/body-policy.sh` from the PR checkout. Since this job is meant to validate untrusted PR text, running the scanner from the same untrusted ref allows a PR to weaken or disable the scan and still produce a green check. Using a trusted ref for the scanner (for PRs, the base SHA) keeps the policy enforcement tamper-resistant.</comment>

<file context>
@@ -71,3 +92,72 @@ jobs:
+        with:
+          # Only the gate's own scripts are needed — no reason to pay for the whole
+          # tree on every comment.
+          sparse-checkout: scripts/public-repo-guard
+          sparse-checkout-cone-mode: false
+
</file context>
Suggested change
sparse-checkout: scripts/public-repo-guard
ref: ${{ github.event.pull_request.base.sha || github.sha }}
sparse-checkout: scripts/public-repo-guard
Fix with cubic

Comment thread .github/workflows/public-repo-guard.yml
# and the org should not pay for a gitleaks run every time anyone posts one) and
# skips `edited` (a title or body edit does not change the tree).
if: >-
(github.event_name == 'pull_request' && github.event.action != 'edited')

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The tree scan now skips every pull_request.edited event, which creates a blind spot for edited actions that are not just body/title text (for example PR metadata changes that can affect what should be validated). Running guard for all PR actions avoids relying on edited being text-only and keeps the scan behavior fail-closed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/public-repo-guard.yml, line 58:

<comment>The tree scan now skips every `pull_request.edited` event, which creates a blind spot for edited actions that are not just body/title text (for example PR metadata changes that can affect what should be validated). Running `guard` for all PR actions avoids relying on `edited` being text-only and keeps the scan behavior fail-closed.</comment>

<file context>
@@ -25,24 +26,44 @@ name: public-repo-guard
+    # and the org should not pay for a gitleaks run every time anyone posts one) and
+    # skips `edited` (a title or body edit does not change the tree).
+    if: >-
+      (github.event_name == 'pull_request' && github.event.action != 'edited')
+      || github.event_name == 'push'
+      || github.event_name == 'workflow_dispatch'
</file context>
Suggested change
(github.event_name == 'pull_request' && github.event.action != 'edited')
github.event_name == 'pull_request'
Fix with cubic

# .github/workflows/public-repo-guard.yml
# .gitleaks.toml
# scripts/public-repo-guard/content-policy.sh
# scripts/public-repo-guard/body-policy.sh

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The install header says to copy four files to set up public-repo-guard in a new repo, but the guard job unconditionally executes a fifth file, scripts/public-repo-guard/tests/body-policy.test.sh, as a required self-test step. A repo that follows the documented four-file install list will fail CI at this step since the test script won't exist. Update the header to include the test script (or gate/remove the self-test step for minimal installs).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/public-repo-guard.yml, line 20:

<comment>The install header says to copy four files to set up public-repo-guard in a new repo, but the guard job unconditionally executes a fifth file, scripts/public-repo-guard/tests/body-policy.test.sh, as a required self-test step. A repo that follows the documented four-file install list will fail CI at this step since the test script won't exist. Update the header to include the test script (or gate/remove the self-test step for minimal installs).</comment>

<file context>
@@ -13,10 +13,11 @@ name: public-repo-guard
 #   .github/workflows/public-repo-guard.yml
 #   .gitleaks.toml
 #   scripts/public-repo-guard/content-policy.sh
+#   scripts/public-repo-guard/body-policy.sh
 #
 # Scan scope: the published working TREE (gitleaks --no-git), NOT git history. The
</file context>
Suggested change
# scripts/public-repo-guard/body-policy.sh
# scripts/public-repo-guard/body-policy.sh
# scripts/public-repo-guard/tests/body-policy.test.sh
Fix with cubic

…d not scan

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 4 new potential issues.

Open in Devin Review

Comment thread scripts/public-repo-guard/body-policy.sh Outdated
Comment thread .github/workflows/public-repo-guard.yml
Comment on lines +118 to +119
body-guard:
name: Body content policy

@devin-ai-integration devin-ai-integration Bot Aug 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 New job's check name is not the required check, so a failing body scan may not block merge

The PR states "On a PR this PREVENTS the merge", but branch protection in these repos requires the check named Secrets + content policy (the guard job). The new job publishes a different check name, Body content policy. Unless the branch-protection rule is updated to also require Body content policy, a blocking body-policy violation will show as a red run but will not gate the merge. Worth confirming the required-checks configuration alongside this PR.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Branch protection required-checks are repository settings, not workflow code; this PR cannot change them. The observation is correct that "Body content policy" must be added to the required checks for the body scan to gate merges, but that is an admin configuration step outside the PR's files.

Comment thread .github/workflows/public-repo-guard.yml Outdated
The primary scan failed closed on rg errors but both follow-up filters
swallowed them with '|| true', collapsing a detection into an empty match
list and a green run. The filters now treat rc >= 2 as exit 2, keeping
rc 1 (every hit allowlisted) as clean.

Also: comment events now scan only the comment body — the enclosing
issue/PR body has its own opened/edited events, and re-scanning it on
every comment would pin a permanent red run to main for one stale leak.
And the changelog gets the Unreleased entry CONTRIBUTING.md requires.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 4 new potential issues.

Open in Devin Review

Comment thread scripts/public-repo-guard/tests/body-policy.test.sh Outdated
Comment on lines +62 to +68
if: >-
(github.event_name == 'pull_request' && github.event.action != 'edited')
|| github.event_name == 'push'
|| github.event_name == 'workflow_dispatch'
concurrency:
group: public-repo-guard-tree-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

@devin-ai-integration devin-ai-integration Bot Aug 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Tree job is now skipped on PR body edits — confirm branch protection treats that as passing

Adding edited to the pull_request types means the workflow now fires on body/title edits, while the guard job's if deliberately skips those runs. A job skipped via if produces a check run with conclusion skipped. GitHub branch protection normally treats a skipped required check as passing, but if "Secrets + content policy" is configured as a required status check it is worth confirming on one PR that an edited event does not leave the PR blocked or reset an already-green check.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread .github/workflows/public-repo-guard.yml
Comment thread scripts/public-repo-guard/tests/body-policy.test.sh Outdated
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
devin-ai-integration[bot]

This comment was marked as resolved.

The \b before OPS_DETAIL could never match after a multi-part credential
name's underscore, so name-then-detail order missed every multi-segment
name; drop it and add a regression fixture.

Key the body-guard concurrency group on comment.id first: review-comment
payloads also carry pull_request, so PR-number-first funneled all inline
comments into one group where GitHub keeps only one pending run, dropping
intermediate comments unscanned.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Two review findings:

pull_request_review was the last unscanned body surface: a review's
top-level summary lives at .review.body, and the old jq fallback would
have scanned the PR body while reporting a verdict on the review. New
trigger (submitted/edited), a review.id term in the concurrency chain so
review bursts cannot drop a pending scan, and an explicit jq branch.

An empty GUARD_PRIVATE_REPOS used to skip the private-repo rule
silently, green-stamping bodies the main rule never examined. GitHub
withholds configuration variables from fork pull_request runs, so the
workflow marks the variable REQUIRED everywhere except fork PRs:
required+empty exits 2 (fail closed), fork+empty skips the one rule with
a visible warning, local runs stay quiet. Three new fixtures pin all
three paths (27 pass).

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 3 new potential issues.

Open in Devin Review

# the private-repo rule with a visible warning. Everywhere else the
# variable must be set, and the script FAILS CLOSED on an empty one
# rather than green-stamping a body its main rule never examined.
GUARD_PRIVATE_REPOS_REQUIRED: ${{ github.event.pull_request.head.repo.fork != true && '1' || '' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Missing private-repo configuration silently downgrades to a warning on review comments from forks

The scan of a code-review comment or review summary on a fork's pull request is marked as "allowed to run without the private-repo list" (github.event.pull_request.head.repo.fork != true at .github/workflows/public-repo-guard.yml:205), even though that list is in fact available for those events, so a missing or renamed list turns the gate's main rule into a warning that passes instead of a red run.
Impact: If the private-repo configuration is ever unset or renamed, review comments naming a private repo alongside operational detail would be reported as clean instead of failing.

Why the fork check is wrong for review-family events

GUARD_PRIVATE_REPOS_REQUIRED is set to '1' only when github.event.pull_request.head.repo.fork != true. For pull_request_review and pull_request_review_comment the payload carries the PR object, so on a fork PR head.repo.fork is true and the flag becomes empty. But unlike pull_request, those events (like issue_comment) run in the base repository's context, where vars.GUARD_PRIVATE_REPOS is available — so an empty value there means real misconfiguration, not a platform limitation.

With the flag empty, scripts/public-repo-guard/body-policy.sh:154-160 takes the warn-and-continue branch instead of exit 2, and the private-repo-ops rule at scripts/public-repo-guard/body-policy.sh:161-186 never runs — the exact fail-open the comment at .github/workflows/public-repo-guard.yml:199-204 says it prevents. Note the inconsistency is visible within the same fork PR: an issue_comment on it has no .pull_request in the payload, so it does get '1' and fails closed.

The condition should only relax for the pull_request event itself, e.g. gate on github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork.

Suggested change
GUARD_PRIVATE_REPOS_REQUIRED: ${{ github.event.pull_request.head.repo.fork != true && '1' || '' }}
GUARD_PRIVATE_REPOS_REQUIRED: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) && '' || '1' }}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +60 to +61
matches="$(printf '%s' "$raw" \
| rg -vN -- 'guard:allow[[:space:]]+[^[:space:]]')"; frc=$?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟨 Any author can disable the body leak gate by adding a guard:allow marker to their own text

scripts/public-repo-guard/body-policy.sh:60-61 drops every line containing guard:allow <reason> before any rule verdict is computed. Unlike the tree scanner, where such a marker lives in a reviewable diff, the scanned text here is the untrusted body of a PR, issue, or comment written by an arbitrary (possibly external) author. Appending guard:allow x to the same line neutralizes all rules on that line, including live-credential formats such as the AWS key ID and private-key header checks. The fixture at scripts/public-repo-guard/tests/body-policy.test.sh:88-89 confirms this bypass is exercised and expected.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +152 to +157
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Only the gate's own scripts are needed — no reason to pay for the whole
# tree on every comment.
sparse-checkout: scripts/public-repo-guard
sparse-checkout-cone-mode: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟨 Body scanner script is executed from the pull request's own checkout, so a fork can neutralize the gate

The body-guard job checks out the event ref (for pull_request this is the fork's merge commit) at .github/workflows/public-repo-guard.yml:152-157 and then runs scripts/public-repo-guard/body-policy.sh from that checkout at .github/workflows/public-repo-guard.yml:206. A fork PR that edits scripts/public-repo-guard/body-policy.sh to exit 0 immediately makes the body gate report green while its own body carries a leak. The same pattern already exists for the tree job's content-policy.sh, so this is not newly introduced, but the new job extends it to the body surface.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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

Labels

rr:skip-coderabbit RF.P1 reviewer routing (#1039)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant