Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/morwn/github-threat-detector/llms.txt

Use this file to discover all available pages before exploring further.

CI/CD pipelines are the most targeted surface in modern supply chain attacks. A workflow that runs with write permissions on pull_request_target can be exploited by any external contributor to exfiltrate secrets, push malicious commits, or publish tampered releases — all from a fork they fully control. The three rules in this category cover the main vectors: direct workflow file modification by unknown actors, fork-originating pull requests that could trigger privileged workflows, and workflow runs that have already executed with fork-supplied code.

workflow-file-change

Severity: high
Required data: Events only

What it detects

A PushEvent that touches any file under .github/workflows/ made by an actor who has no prior push history in the repository over the past 30 days. Actors with at least one push in the 30-day window are classified as known maintainers and suppressed.

How detection works

The analyzer builds a maintainers CTE from all actors who pushed to the repository in the past 30 days. It then queries for push events whose payload mentions /.github/workflows/ and left-joins against that maintainer set. Any row where the join finds no match — meaning the actor is not a known maintainer — is emitted as a finding.
-- Simplified logic
WITH maintainers AS (
    SELECT DISTINCT actor_login
    FROM github_events
    WHERE event_type = 'PushEvent'
      AND created_at < now() - INTERVAL '30 days'
),
workflow_pushes AS (
    SELECT id, repo_name, actor_login, created_at, payload
    FROM github_events
    WHERE event_type = 'PushEvent'
      AND payload::text ILIKE '%/.github/workflows/%'
)
SELECT wp.*
FROM workflow_pushes wp
LEFT JOIN maintainers m ON wp.actor_login = m.actor_login
WHERE m.actor_login IS NULL

Finding description

Actor <actor_login> pushed workflow file changes but has no prior commit history

Evidence fields

FieldDescription
created_atTimestamp of the push event
Any modification to a workflow file by an unrecognized actor is a serious signal. An attacker who gains temporary write access (or abuses a bot token) can insert malicious steps that run on the next triggered workflow, exfiltrate GITHUB_TOKEN, or add a backdoor to build artifacts.Remediation: Enable branch protection rules that require pull request reviews for changes to .github/workflows/. Restrict who can approve such PRs to a named set of maintainers.

pr-target-abuse

Severity: critical
Required data: Events only

What it detects

A PullRequestEvent with action: opened where the pull request originates from a fork (pull_request.head.repo.fork = true). This pattern is the precursor to pull_request_target privilege escalation.

How detection works

The analyzer queries for opened pull request events where the head repository is marked as a fork. Because pull_request_target runs in the context of the base repository’s secrets — not the fork — any workflow that triggers on this event and checks out fork code can be exploited to access those secrets.
SELECT id, repo_name, actor_login, created_at, payload
FROM github_events
WHERE event_type = 'PullRequestEvent'
  AND payload->'pull_request'->'head'->'repo'->>'fork' = 'true'
  AND payload->>'action' = 'opened'

Finding description

Fork PR opened by <actor_login> -- potential pull_request_target privilege escalation

Evidence fields

FieldDescription
created_atTimestamp of the event
pr_numberPull request number
head_repoFull name of the fork repository (e.g. attacker/target-repo)
This is a critical-severity finding. Every fork PR opened against a repository that uses pull_request_target workflows is a potential secret exfiltration attempt. Even if the specific PR is benign, the presence of this trigger in your workflows is a standing vulnerability.Remediation: Audit all pull_request_target workflows in your repository. Ensure none of them check out the fork’s code using actions/checkout@v4 with ref: ${{ github.event.pull_request.head.sha }}. If you need to run CI on fork PRs, use pull_request (which runs with no write permissions) instead. If pull_request_target is required for comment posting, scope the checked-out ref to the base branch only.

Real-world context: aquasecurity/trivy (February 2025)

The pr-target-abuse pattern was exploited against the aquasecurity/trivy repository in February 2025 (PR #10254). The repository’s CI workflow used pull_request_target to run tests on fork-supplied code while retaining access to repository secrets. An external actor submitted a pull request from a fork that, when the workflow triggered, would have allowed execution of attacker-controlled code in the privileged runner context. The pr-target-abuse rule is designed to surface exactly this scenario as soon as the pull request is opened — before any workflow executes.

workflow-run-from-fork

Severity: critical
Required data: Workflow runs (--actions)

What it detects

A completed workflow run that was triggered by the pull_request_target event and whose head repository is an external fork — and where the triggering actor is neither in the contributor list nor has any prior push history in the repository.

How detection works

This rule operates on the workflow_runs table populated by --actions collection. Unlike pr-target-abuse, which fires at PR open time, this rule fires when the workflow has actually run. It additionally cross-references the contributor list (repo_contributors) and push history to filter out trusted actors, and excludes bot accounts.
SELECT wr.id, wr.repo_name, wr.actor_login, wr.created_at, wr.payload
FROM workflow_runs wr
WHERE wr.event = 'pull_request_target'
  AND (wr.payload->'head_repository'->>'fork')::boolean IS TRUE
  AND wr.actor_login NOT LIKE '%-bot'
  AND wr.actor_login NOT LIKE '%[bot]'
  AND NOT EXISTS (
      SELECT 1 FROM repo_contributors rc
      WHERE rc.repo_name = wr.repo_name
        AND rc.actor_login = wr.actor_login
  )
  AND NOT EXISTS (
      SELECT 1 FROM github_events ge
      WHERE ge.repo_name = wr.repo_name
        AND ge.actor_login = wr.actor_login
        AND ge.event_type = 'PushEvent'
  )

Finding description

Workflow run triggered by pull_request_target from fork by <actor_login>

Evidence fields

FieldDescription
workflow_run_idInternal ID of the workflow run record
created_atTimestamp when the run was created
head_shaCommit SHA from the fork that was executed
Collect contributor data alongside workflow runs for the most accurate results. Without --contributors, trusted actors who have contributed via means other than direct pushes (e.g. via accepted PRs) may appear as false positives.
github-threat-detector collect --repo owner/repo --actions --contributors
A finding here means attacker-controlled code has already executed in your privileged runner. Immediately audit the workflow run logs for secret access, outbound network calls, and any artifact publishing steps. Rotate any secrets accessible to that runner.

Build docs developers (and LLMs) love