Skip to main content

What the check does

Flags a job that runs on a self-hosted runner and does at least one sensitive thing, with no egress policy declared.

Why it matters

A GitHub-hosted runner is a fresh VM destroyed after the job. A self-hosted runner is a machine on your network, and by default it can reach the whole internet with the job’s secrets in scope. That is the shape exfiltration takes when it does not need an exploit: the job legitimately holds a deploy key and legitimately makes network calls, and nothing distinguishes the call that ships the build from the call that ships the key.

Why this is not best-prac-3-self-hosted-runners

That rule reports the fact of a self-hosted runner, tiered to the auditor persona because it is usually a deliberate infra choice. This rule fires only where the combination bites — self-hosted and something worth stealing: A self-hosted job that only runs make lint stays quiet. So does a GitHub-hosted job holding secrets.

Two ways to satisfy it

1. Declare a policy in the workflow

step-security/harden-runner is the one egress policy declarable inside a workflow file, so the rule reads it:
egress-policy: audit does not satisfy it — audit mode observes egress without restricting it. Rather than staying silent, the finding names what it found:
…the job runs step-security/harden-runner with egress-policy: audit, which observes egress but does not restrict it.

2. Record that egress is governed elsewhere

Firewalls, network policies, and proxies live in runner infrastructure the scanner cannot see. So the rule ships its own acknowledgment:
Nesting is the scope. An unindented directive covers the file; one placed above a job covers that job. This is a dedicated directive rather than the generic # pipefort: ignore[...] because the two mean different things. ignore says “do not tell me about this”. egress-restricted says “this is handled” — and it reads that way to the next person in the diff.

GitLab CI

The same rule ID. GitLab has no self-hosted label — a job reaches a specific runner through tags:, so a tag that is not one of GitLab’s saas-* runner names means somebody’s own machine. Sensitivity is a CI_*_TOKEN-family or credential-shaped variable reference, or a publish command.

Auto-fix

None — advisory. Egress control lives in runner infrastructure, not in the workflow file, so there is nothing for a fixer to write. The two surfaces above are how you resolve the finding.