Port pr-security-preflight review-thread resolution to the pack - #5
Conversation
react_on_rails' pr-security-preflight had diverged ahead of the pack with generic security improvements (#4148/#4151/#4170). This brings the portable pack to parity on the GENERIC pieces, leaving RoR's hosted-CI metadata recognition (repo-specific labels, script paths, comment vocabulary) as a RoR-local override per the portability seam. Adds: - Review-thread resolution: a resolved trusted-bot review comment is treated as historical review metadata and suppressed; unresolved or untrusted-resolved comments still block; fail-closed if resolution state cannot be fetched. (RESOLVED_REVIEW_THREADS_QUERY + resolved_review_threads_page, add_resolved_review_comment_ids, next_review_threads_cursor, resolved_review_comment_database_ids; rest_context populates the id set, suspicious_pr_review_comments consumes it.) - Untrusted-interaction decomposition: the monolithic untrusted_interaction_findings split into per-surface helpers (issue comments / review comments / reviews), minus the hosted-CI suppression hook. No repo-specific strings enter the pack (verified by grep). Upstream goes 13 -> 16 preflight tests with new LANG=C-safe fixtures; bin/validate green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 20 minutes and 35 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 28325b3a68
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| nodes { | ||
| isResolved | ||
| resolvedBy { login } | ||
| comments(first:100) { |
There was a problem hiding this comment.
Paginate resolved review thread comments
When a resolved review thread has more than 100 comments, this query only returns the first 100 databaseIds. The REST review-comment scan still inspects all pull comments, so a suspicious trusted-bot comment later in the same resolved thread is absent from resolved_review_comment_ids and incorrectly remains a blocker despite the thread being resolved by a trusted actor.
Useful? React with 👍 / 👎.
Security Preflight Review — PR #5This PR ports review-thread resolution suppression from react_on_rails to the portable pack: resolved trusted-bot review comments are now treated as historical review metadata and skipped during the suspicious-text scan. The porting is disciplined — the RoR-specific hosted-CI path was intentionally left out, the pagination loop is correct, the fail-closed fallback ( 1 · Resolved-bot comments are suppressed from warnings too — is that intended? (medium)
See inline comment on 2 ·
|
| # Resolved trusted-bot review comments are historical review metadata. Keep | ||
| # unresolved/current bot findings blocking, and fail closed if resolution | ||
| # state could not be fetched. | ||
| next if trusted_bot?(author, trust_config) && resolved_review_comment_ids.include?(comment["id"]) |
There was a problem hiding this comment.
This guard fires before the pattern test, so it suppresses the comment from both the blocking-findings pass and the warning-findings pass. A resolved trusted-bot comment containing GITHUB_TOKEN (warning-only) produces zero output — no blocking finding and no warning.
If the intent is to treat all content from resolved bot threads as historical metadata (including warning-level content), the inline comment should say so. If warnings should still surface for resolved threads, the guard needs to be conditional on the active pattern:
| next if trusted_bot?(author, trust_config) && resolved_review_comment_ids.include?(comment["id"]) | |
| next if trusted_bot?(author, trust_config) && resolved_review_comment_ids.include?(comment["id"]) && | |
| pattern == BLOCKING_SUSPICIOUS_PATTERN |
Or add a # Both blocking and warning findings are suppressed for resolved trusted-bot comments. note to make the current intent unambiguous.
| nodes { | ||
| isResolved | ||
| resolvedBy { login } | ||
| comments(first:100) { |
There was a problem hiding this comment.
comments(first:100) fetches at most 100 nodes per thread with no inner cursor or pagination loop. A thread with more than 100 comment replies will have some databaseIds silently excluded from resolved_review_comment_ids.
The consequence is fail-closed — missed IDs remain blocking — so this isn't a security bypass. But it is a silent truncation. Consider a # NOTE: threads with >100 comments are fail-closed — missing IDs stay blocking comment, or a hasNextPage check in add_resolved_review_comment_ids that warns rather than silently drops.
| Open3.capture2e(env, "ruby", SCRIPT, *) | ||
| end | ||
|
|
||
| def trust_coderabbit(trust_config_path) |
There was a problem hiding this comment.
This helper bakes coderabbitai into the shared pack's test, coupling a generic fixture to a specific vendor's product. The two pre-existing tests that need a trusted bot (e.g. test_hidden_trusted_bot_participant_is_allowed) spell out the YAML inline, which keeps the product name visible and local to the call site.
Consider a generic helper instead:
| def trust_coderabbit(trust_config_path) | |
| def write_trust_config(trust_config_path, bots: [], users: ["justin808"]) | |
| File.write(trust_config_path, <<~YAML) | |
| trusted_users: | |
| #{users.map { |u| "- #{u}" }.join("\n ")} | |
| trusted_bots: | |
| #{bots.map { |b| "- #{b}" }.join("\n ")} | |
| trusted_teams: [] | |
| YAML | |
| end |
Call sites become write_trust_config(trust_config_path, bots: ["coderabbitai"]), keeping the product name explicit at each test rather than hidden inside the helper name.
| next if trusted_actor?(repo, author, trust_config, team_cache) | ||
| def untrusted_issue_comment_findings(rest_context, repo, trust_config:, team_cache:) | ||
| rest_context.fetch(:issue_comments).filter_map do |comment| | ||
|
|
There was a problem hiding this comment.
Spurious blank line after do |comment| — the two peer functions (untrusted_review_comment_findings, untrusted_review_findings) omit it. Remove for consistency.
| rest_context.fetch(:issue_comments).filter_map do |comment| |
| cat <<'JSON' | ||
| {"number":123,"title":"Tëst issué — café","html_url":"https://github.com/owner/repo/issues/123","body":"Café au lait notes — déjà vu 🚀 friendly documentation update","user":{"login":"justin808"}} | ||
| JSON | ||
| elif [ "$mode" = "resolved-trusted-bot-review-comment" ] || [ "$mode" = "untrusted-resolver-trusted-bot-review-comment" ] || [ "$mode" = "unresolved-trusted-bot-review-comment" ]; then |
There was a problem hiding this comment.
This [ "$mode" = "resolved-..." ] || [ "$mode" = "untrusted-..." ] || [ "$mode" = "unresolved-..." ] condition appears verbatim at 5 points in the generated bash script (lines 339, 374, 426, 464, 472). Adding a fourth mode in this group requires editing all five heredoc sites with no safety net.
A shell helper hoisted to the top of the generated script would collapse each site:
is_bot_review_comment_mode() {
[ "$mode" = "resolved-trusted-bot-review-comment" ] || \
[ "$mode" = "untrusted-resolver-trusted-bot-review-comment" ] || \
[ "$mode" = "unresolved-trusted-bot-review-comment" ]
}Then each branch becomes if is_bot_review_comment_mode; then.
Brings the portable pack's
pr-security-preflightto parity with react_on_rails' copy (which had diverged ahead via #4148/#4151/#4170) — porting only the GENERIC security improvements and leaving RoR's hosted-CI metadata recognition as a RoR-local override.Ported (generic, repo-agnostic)
RESOLVED_REVIEW_THREADS_QUERY+resolved_review_threads_page/add_resolved_review_comment_ids/next_review_threads_cursor/resolved_review_comment_database_ids;rest_contextpopulates the id set,suspicious_pr_review_commentsconsumes it.)untrusted_interaction_findingssplit into per-surface helpers (issue comments / review comments / reviews).Intentionally left out (RoR-specific)
The
hosted_ci_*metadata recognition encodes RoR's labels (ready-for-hosted-ci), script path (script/ci-changes-detector), and comment vocabulary — textbook repo-specific seam values. Porting it would ship dead, trust-widening machinery into a portable pack (YAGNI; one consumer). RoR keeps it as a thin local override.Verification
bin/validategreen; preflight suite 13 → 16 tests (resolved / unresolved / untrusted-resolver cases with new fake-gh fixtures).hosted ci, the labels, the script path,github_actions_bot, …).scan_targetkeeps upstream's directparticipant_findingscall (noparticipant_findings_for_target, nohosted_ci_metadata_actors).