feat(review): add review-evasion protection#3414
Merged
Merged
Conversation
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
gittensory-ui | 65cd80a | Commit Preview URL Branch Preview URL |
Jul 05 2026, 08:15 AM |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3414 +/- ##
==========================================
+ Coverage 93.01% 93.04% +0.03%
==========================================
Files 297 297
Lines 31038 31193 +155
Branches 11319 11383 +64
==========================================
+ Hits 28870 29024 +154
Misses 1513 1513
- Partials 655 656 +1
🚀 New features to boost your workflow:
|
|
Important 🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪 🔍 Gittensory is reviewing…AI analysis is in progress. This comment will update when the review is complete. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed · 🟪 Reviewing |
10 tasks
This was referenced Jul 5, 2026
This was referenced Jul 5, 2026
Contributors were closing or converting PRs to draft immediately after Gittensory started reviewing them, dodging the one-shot terminal decision. Track when a fresh review pass starts against a PR's head, and when a contributor closes or drafts their own PR while that tracking is still active, reopen (if needed) and re-close it as the App -- a close the contributor cannot themselves reopen -- post an explanation, apply the configured label, and record a review_evasion moderation strike once the enforcement close actually succeeds. Wires review_evasion into the shared moderation-rules engine and adds reviewEvasionProtection/reviewEvasionLabel/reviewEvasionComment config-as-code settings (DB, types, resolver, OpenAPI, .gittensory.yml). Validated with npm run test:coverage, npm run test:ci, and npm audit --audit-level=moderate.
The draft-conversion handler only checked pr.authorLogin against the maintainer/owner/bot exemptions -- it never compared the webhook sender to the author, so a maintainer converting a CONTRIBUTOR's PR to draft during an active review was wrongly treated as self-evasion and enforced against the author who didn't do it. Require sender.login === authorLogin before enforcing, matching the self-close handler's existing actor check. Also keeps the migration's already-updated ISO-8601 strftime defaults (matching src/db/schema.ts's nowIso() convention) and adds a v8-ignore for terminalizeActiveReviewTracking's defensive D1-metadata fallback. Validated with npm run test:ci and npm audit --audit-level=moderate.
closeReviewEvasionSelfCloseIfActive audited an error and returned normally when reopenPullRequest succeeded but the subsequent closePullRequest call failed, silently leaving the PR OPEN on GitHub -- worse than the contributor's original close, and the exact outcome this enforcement exists to prevent. Propagate the error instead so the queue's own retry/backoff re-processes the job; the live freshness check already in this handler sees the PR as open (current) on retry and re-attempts the close, converging once it succeeds. Also tightens patch coverage: v8-ignore annotations on two provably unreachable branches, and named intermediate variables for the comment-gating checks in both evasion handlers. Validated with npm run test:ci and npm audit --audit-level=moderate.
e9c401f to
65cd80a
Compare
24 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
active_review_tracking, one row per repo/PR, scoped to the reviewed head) that starts right before cost-bearing AI-review work begins and terminalizes when the pass concludes, the head changes, the PR closes, or evasion enforcement completes.review_evasionmoderation strike, but only once the enforcement close actually succeeds.applyModerationEscalationForRulehelper so both the existing planner-driven path and these new direct webhook handlers use identical logic.review_evasioninto the moderation-rules engine (own event type, includable/excludable via global or per-repomoderationRules) and addsreviewEvasionProtection/reviewEvasionLabel/reviewEvasionCommentas full config-as-code settings (DB column + Drizzle schema,RepositorySettingstype, DB row parse/write,.gittensory.ymlresolver, OpenAPI schema, documented.gittensory.yml.exampleblock). Off by default -- zero behavior change for an install that hasn't opted in.No issue linked: this is a new capability (anti-abuse protection), not a bug fix, and there is no existing open issue tracking it.
Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlintnpm run typechecknpm run test:coveragelocally;codecov/patchrequires ≥99% coverage of the lines AND branches you changed (aim for 100% on your diff so CI variance does not fail near the threshold). Global coverage is a non-blocking trend with a loose 90% backstop, not the gate.npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderateAll of the above ran green via
npm run test:ciplusnpm audit --audit-level=moderate, both locally and after rebasing onto the latestmain.Safety
UI Evidencesection below with screenshots. (n/a -- backend-only change, no visible UI).gittensory.yml.exampledocuments the new settings;CHANGELOG.mdintentionally untouched)Notes
test/unit/queue.test.ts(self-close and converted_to_draft evasion handlers, including lock contention, dry-run, pause/freeze, write-permission denial, live-freshness staleness, reopen/close API failure, redelivery idempotency, and the interaction with the existing one-shot reopen guard),test/unit/agent-action-executor.test.ts(the sharedapplyModerationEscalationForRulehelper),test/unit/db-persistence.test.tsandtest/unit/moderation-config-db.test.ts(the newactive_review_trackingrepository functions and the new settings' DB round-trip),test/unit/moderation-rules.test.tsandtest/unit/focus-manifest.test.ts(the new rule type and yml parsing),test/unit/github-pr-actions.test.ts(reopenPullRequest).