High Number of Closed Pull Requests by User
editHigh Number of Closed Pull Requests by User
editDetects a high number of closed pull requests by a single user within a short time frame. Adversaries may close multiple pull requests to disrupt development workflows or hide malicious changes.
Rule type: esql
Rule indices: None
Severity: medium
Risk score: 47
Runs every: 8m
Searches indices from: now-9m (Date Math format, see also Additional look-back time)
Maximum alerts per execution: 100
References:
Tags:
- Domain: Cloud
- Use Case: Threat Detection
- Tactic: Impact
- Tactic: Exfiltration
- Data Source: Github
- Resources: Investigation Guide
Version: 2
Rule authors:
- Elastic
Rule license: Elastic License v2
Investigation guide
edit## Triage and analysis
Disclaimer: This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
Investigating High Number of Closed Pull Requests by User
This rule flags a single user rapidly closing many pull requests in a short window, a disruptive pattern that suppresses review history, delays releases, and masks unauthorized changes. An attacker with stolen maintainer access mass-closes pull requests across multiple repositories, then force-pushes branches and opens new pull requests that sidestep earlier review threads, making malicious edits appear routine amid churn.
Possible investigation steps
- Determine if the actor is a bot or sanctioned maintenance by confirming account type, scheduled workflows, and change advisories from repo/org owners.
- Open a sample of the closed PRs to review comments, labels, linked issues, and whether closure coincided with branch deletions, force-pushes, or unusual commit history in the target branches.
- Correlate the closure burst with audit events for permission changes, role assignments, repository settings edits, or protection rule modifications to detect potential sabotage.
- Validate the actor’s IPs, geolocation, and user agents against baselines and check for recent PAT creations, OAuth app grants, or SSO anomalies indicating credential theft.
- Identify whether closed PRs were immediately replaced by new PRs carrying similar diffs that bypass prior review threads and required checks, and verify branch protection remained enforced.
False positive analysis
- A maintainer or org-owned bot performs scheduled backlog hygiene, closing stale, duplicate, or superseded PRs across multiple repositories after a default branch rename or policy update, resulting in a high closure count from one account.
- During a planned migration or archival, a release manager closes PRs tied to deprecated branches and consolidates work into new targets, legitimately generating a burst of closures attributed to a single user.
Response and remediation
- Immediately contain by removing the user from teams with Triage/Write permissions on affected repositories, revoking their personal access tokens from Tokens & keys, and tightening branch protection by disallowing force-pushes and restricting who can push to main and release branches.
- Trigger escalation to Security Incident Response if closed pull requests span more than five repositories within one hour, coincide with branch deletions or forced pushes, or originate from a new user agent/IP, and disable the account at the identity provider while contacting GitHub Support.
- Eradicate impact by reopening legitimate PRs via each closed PR URL, using Restore branch or recreating the head branch from the last known commit SHA, and reapplying required labels and reviewers.
- Recover repository state by comparing diffs of closed PRs to any newly opened PRs by the same user, reverting unauthorized commits in target branches with git revert, and re-running required status checks before merging.
- Harden controls by enforcing branch protection rules (require two approvals, restrict who can dismiss reviews, require signed commits), enabling CODEOWNERS for critical paths, and turning off Allow deletions on default and release branches.
- Prevent recurrence by disabling classic PATs and requiring short-lived fine-grained PATs, revoking unusual OAuth app grants, mandating SSO with hardware-backed MFA, and installing a GitHub App/Action that notifies on PR closures with PR URLs, repos, and branches and requires a reason-coded label per policy.
Rule query
editfrom logs-github.audit-* metadata _id, _index, _version | where data_stream.dataset == "github.audit" and github.category == "pull_request" and event.type == "change" and event.action == "pull_request.close" | stats Esql.document_count = COUNT(*), Esql.github_org_values = values(github.org), Esql.github_repo_values = values(github.repo), Esql.github_user_agent_values = values(github.user_agent), Esql.github_pull_request_url_values = values(github.pull_request_url), Esql.user_name_values = values(user.name), Esql.agent_id_values = values(agent.id), Esql.event_dataset_values = values(event.dataset), Esql.data_stream_namespace_values = values(data_stream.namespace) by user.name | keep Esql.* | where Esql.document_count >= 10
Framework: MITRE ATT&CKTM
-
Tactic:
- Name: Impact
- ID: TA0040
- Reference URL: https://attack.mitre.org/tactics/TA0040/
-
Technique:
- Name: Data Destruction
- ID: T1485
- Reference URL: https://attack.mitre.org/techniques/T1485/
-
Tactic:
- Name: Exfiltration
- ID: TA0010
- Reference URL: https://attack.mitre.org/tactics/TA0010/
-
Technique:
- Name: Automated Exfiltration
- ID: T1020
- Reference URL: https://attack.mitre.org/techniques/T1020/
-
Technique:
- Name: Exfiltration Over Web Service
- ID: T1567
- Reference URL: https://attack.mitre.org/techniques/T1567/
-
Sub-technique:
- Name: Exfiltration to Code Repository
- ID: T1567.001
- Reference URL: https://attack.mitre.org/techniques/T1567/001/