> For the complete documentation index, see [llms.txt](https://docs.stepsecurity.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.stepsecurity.io/github-actions/workflow-run-policies/policies.md).

# Policies

Use this page to view and manage all workflow run policies created in your organization.

<figure><img src="/files/WVimDO2zBUQERxPVp76T" alt=""><figcaption></figcaption></figure>

## Creating a New Policy in Your Organization

There are five policy types you can create:

* [Compromised Actions Policy](#compromised-actions-policy) - Block the use of compromised Actions
* [Secret Exfiltration Policy ](#secret-exfiltration-policy)- Prevents unauthorized access to Secrets
* [Allowed Actions Policy ](#allowed-actions-policy)- Block specific GitHub Actions
* [Runner Label Policy](#runner-label-policy) - Prevent or monitor usage of specific runners
* [Harden-Runner Policy](#harden-runner-policy) - Require the Harden-Runner action in every workflow run

### Compromised Actions Policy

The Compromised Actions Policy prevents the use of known malicious or compromised GitHub Actions in workflows. It scans for references to actions flagged as security risks and blocks their execution to protect your environment.

#### **Why It Matters**

Workflows often rely on third-party actions, which may be:

* Compromised via account takeovers
* Malicious by design
* Altered to include malware or backdoors

This policy reduces risk by:

* Maintaining a list of compromised actions
* Scanning workflows for those references
* Blocking runs using them
* Alerting developers with actionable feedback

#### **How It Works**

StepSecurity's threat intelligence team continuously monitors the GitHub Actions ecosystem for compromised actions. When the team learns that an action has been compromised, it manually verifies the compromise and then adds the action to the compromised actions list that StepSecurity maintains.

Once an action is on the list, any new workflow run that uses it is blocked from execution. StepSecurity sends a cancel API call to the workflow run, so the run is canceled before the compromised action can do harm.

**Lifecycle of a compromised action event**

1. **Discovery**: The threat intelligence team learns of a potential compromise through continuous monitoring.
2. **Verification**: The team manually verifies the compromise before taking action.
3. **Blocking**: The action is added to the compromised actions list. New workflow runs that reference it are canceled automatically.
4. **Notification**: Each addition to the list is accompanied by a [Threat Center](/workspace/threat-center.md) entry and a threat intel notification, so you are alerted when such an event happens.
5. **Return to normal operations**: Once the action's repository has been cleaned up, blocking is limited to the specific compromised commit SHAs only. The action as a whole is no longer blocked, and workflows referencing clean versions run normally.

**Viewing the current list**

The policy editor shows the compromised actions the policy currently checks for, under **Currently known compromised actions**. Each entry shows whether all versions of the action are compromised or only specific versions (for example, release tags that were re-pointed to a malicious commit). The list updates automatically as StepSecurity's threat intelligence team adds new compromises, so the count and entries you see reflect the live list.

<figure><img src="/files/AXLS8PHEmdgmRFjg2ggf" alt=""><figcaption></figcaption></figure>

**Point-in-time evaluation records**

Every policy evaluation records the compromised actions list as it existed at the time of the run. When you review a past evaluation on the Policy Evaluations page, you can open **Compromised actions list at run time** to see exactly which entries the run was checked against. Actions added to the list after that run are not shown, so you can always reconstruct why a run was or was not blocked.

**What Developers See**

If a compromised action is used:

* The workflow is automatically canceled and a PR comment explains the violation and suggests trusted alternatives

<figure><img src="/files/YHwG0Vd6HNL33eHxy23t" alt=""><figcaption></figcaption></figure>

To try the Compromised Action policy, add this action to your workflow:

```
step-security/dummy-compromised-action@main
```

**To create a compromised Actions policy, follow the steps below:**

**Step 1: Navigate to the Workflow Run Policies page**

* Go to your StepSecurity dashboard, then to Workflow Run Policies → Policies in the sidebar.

<figure><img src="/files/3F4wzy6PKnxSo1urwwvz" alt=""><figcaption></figcaption></figure>

**Step 2: Click “Create Policy”**

* Click the Create Policy button on the top right of the page.

<figure><img src="/files/bG1KQuLHZydkq3aTiWY7" alt=""><figcaption><p>Click the create button</p></figcaption></figure>

**Step 3: Fill in Policy Details**

* Policy Name – e.g., *Compromised Actions Policy*
* Policy Type – Select "Compromised Actions Policy"
* Action – Choose between:
  * Enforce: Actively blocks compromised Actions
  * Dry Run: Does not block the workflow run but records the violation in the Evaluations page

<figure><img src="/files/232W30FCQXaYom6g2yte" alt=""><figcaption><p>Setting up Compromised Actions Policy</p></figcaption></figure>

**Step 4: Review the currently known compromised actions**

The policy editor lists the compromised actions the policy will check for, under **Currently known compromised actions**, with the affected versions of each. No configuration is needed here: StepSecurity maintains this list and updates it automatically, so it is shown for review only.

<figure><img src="/files/jr56GabPDu56txbMG6lH" alt=""><figcaption></figcaption></figure>

**Step 5: (Optional) Customize the PR Comment Template**

The editor includes a PR Comment Template used when this policy blocks a run from a pull request. Leave the default unchanged to use the standard StepSecurity comment, or customize it. See [PR Comment Template](#pr-comment-template) for the default template and supported placeholders.

**Step 6: Select Repositories/Organizations**

* Choose whether to apply the policy to:
  * All current and future repositories/organizations *(default)*, or
  * Select specific repositories/organizations manually

<figure><img src="/files/X4OKajZoIbY6va5Jk5Fp" alt=""><figcaption></figcaption></figure>

**Step 5: Save the Policy**

* After configuring all settings, click Save to create the policy.

![Click the save button](https://ajeuwbhvhr.cloudimg.io/https://colony-recorder.s3.amazonaws.com/files/2025-05-05/83faa3ac-1ad1-4d61-8a12-95ced8eee9d3/ascreenshot.jpeg?tl_px=195,859\&br_px=1724,1714\&force_format=jpeg\&q=100\&width=1120.0\&wat=1\&wat_opacity=1\&wat_gravity=northwest\&wat_url=https://colony-recorder.s3.amazonaws.com/images/watermarks/8B5CF6_standard.png\&wat_pad=524,494)

Follow this interactive demo to see how this workflow run policy works in practice:

{% embed url="<https://app.storylane.io/share/ywepexcm8irs>" %}

### Secret Exfiltration Policy

The Secret Exfiltration Policy protects against unauthorized secret access in GitHub Actions. It blocks modified workflows in non-default branches from using secrets unless explicitly approved.

#### **Why It Matters**

Workflows often need secrets to access protected resources. Attackers may exploit non-default branches to run malicious workflows and exfiltrate secrets. This policy helps stop that by:

* Allowing secret access only if the workflow matches the default branch
* Enforcing approval for legitimate changes
* Creating an auditable approval trail

**How It Works**

* Detects non-default branch workflows accessing secrets (${{ secrets.X }}, toJSON(secrets))
* Compares workflow content to the default branch using SHA256
* Requires a workflows-approved label from a different team member for approval
* Blocks runs without proper matching or approval

**What Developers See**

If a modified workflow accesses secrets:

* The run is canceled, and a PR comment explains the block and how to get approval (a teammate has to add the "**workflows-approved**" label to the PR)

<figure><img src="/files/M6NDrlnqDNbn0Xrfr5hj" alt=""><figcaption></figcaption></figure>

* Once the "workflows-approved" label has been added by a teammate, the workflow can be re-run

<figure><img src="/files/juM2lsAUatnuJdVLlAKj" alt=""><figcaption></figcaption></figure>

#### Creating a **Secret Exfiltration** Policy

**Step 1: Select “Secret Exfiltration Policy”**

* You can exempt specific users or bot accounts from this policy. When a workflow is triggered by an exempted user or bot, the run will not be blocked by the policy.

<figure><img src="/files/a4566viUQbdyCqo4FXJU" alt=""><figcaption></figcaption></figure>

**Step 2: (Recommended) Block only bulk secrets access**

Toggle **Block only when a workflow accesses all secrets at once** on to block only pull requests that introduce bulk secret access patterns such as `${{ toJSON(secrets) }}`, which dump every secret at once. Targeted references such as `${{ secrets.NPM_TOKEN }}` are not blocked.

Most real-world CI/CD secret exfiltration attacks use bulk-access patterns. This mode provides targeted protection while dramatically reducing false positives, so you do not need to exempt repositories whose workflows legitimately reference individual secrets.

<figure><img src="/files/VytVbOw9aVY2ioQ86Dif" alt=""><figcaption></figcaption></figure>

**Step 3: (Optional) Analyze default branch runs**

Toggle **Analyze default branch runs** on to also block workflow runs on the default branch when the workflow references secrets. The diff-vs-default-branch comparison and the `workflows-approved` label do not apply on the default branch.

{% hint style="info" %}
We recommend pairing this option with the bulk-access toggle above to limit the blast radius, since workflows on the default branch commonly make legitimate use of individual secrets.
{% endhint %}

**Step 4: (Optional) Customize the PR Comment Template**

The editor includes a PR Comment Template used when this policy blocks a run from a pull request. Leave the default unchanged to use the standard StepSecurity comment, or customize it. [See PR Comment Template](#pr-comment-template) for the default template and supported placeholders.

**Step 5: Choose Target Repositories/Organizations and save the policy**

<figure><img src="/files/LH1RhSudhVQGEKzFGcPW" alt=""><figcaption></figcaption></figure>

Follow this interactive demo to see how this workflow run policy works in practice:

{% embed url="<https://app.storylane.io/share/ehzs59gzdatf>" %}

### Allowed Actions Policy

Use this policy to enforce an allowlist of GitHub Actions. Any action not on the allowlist is blocked (Enforce) or flagged (Dry Run). You can also require that every allowed action be pinned to a specific commit SHA, which protects against tag-overwrite and supply-chain attacks.

#### Why It Matters

Every third-party GitHub Action that runs in your workflow inherits access to your repository's `GITHUB_TOKEN` and any secrets the job exposes. An unvetted, typosquatted, or compromised action can:

* Exfiltrate secrets, tokens, or source code
* Inject malicious code into build artifacts
* Pivot into internal systems via repository-scoped credentials
* Execute arbitrary commands with the permissions of the workflow

Maintaining an allowlist narrows your exposure to the set of actions your security team has reviewed. Requiring pinned references on top of that protects you from a second category of attack: a previously-trusted action whose `@v4` tag or `@main` branch is moved to point at malicious code — the pattern behind the `tj-actions/changed-files` compromise and similar incidents.

#### Creating an **Allowed Actions** Policy

**Step 1: Select “Allowed Actions Policy”**

<figure><img src="/files/yMlIJGu6ki4fPH3UKsKj" alt=""><figcaption></figcaption></figure>

**Step 2: (Optional) Require pinned actions**

Toggle **Only allow pinned actions** on if you want to block any action reference that is not pinned to a commit SHA. This applies on top of the allowlist — an action must be both allowlisted and pinned to pass.

<figure><img src="/files/e8MLIOxTK5SoeuFxH6Oo" alt=""><figcaption></figcaption></figure>

**Step 3: Add Actions to Allowlist**

* Manually type and add actions (e.g., `actions/checkout`) **OR**

<figure><img src="/files/dNRp9XzrjuC5kq9jjWPG" alt=""><figcaption></figcaption></figure>

* Use All Actions (Used) to select from known usage

<figure><img src="/files/cEk0K9N6ejBYeC5XedQ8" alt=""><figcaption></figcaption></figure>

* Select one Action and click "Add to Allowed List"

![Setting up Actions Policy using existing Action in the organization](https://ajeuwbhvhr.cloudimg.io/https://colony-recorder.s3.amazonaws.com/files/2025-05-05/560f6dc1-6ed8-4b44-a0a8-e713fe4e4e96/ascreenshot.jpeg?tl_px=272,0\&br_px=3024,1538\&force_format=jpeg\&q=100\&width=1120.0\&wat=1\&wat_opacity=1\&wat_gravity=northwest\&wat_url=https://colony-recorder.s3.amazonaws.com/images/watermarks/8B5CF6_standard.png\&wat_pad=809,166)

* Decide whether to allow all versions (default) or select specific commit versions **OR**

![Setting up Actions Policy using existing Action in the organization](https://ajeuwbhvhr.cloudimg.io/https://colony-recorder.s3.amazonaws.com/files/2025-05-05/5131ed95-a2d4-4344-b512-835a27697f51/ascreenshot.jpeg?tl_px=0,12\&br_px=2752,1551\&force_format=jpeg\&q=100\&width=1120.0\&wat=1\&wat_opacity=1\&wat_gravity=northwest\&wat_url=https://colony-recorder.s3.amazonaws.com/images/watermarks/8B5CF6_standard.png\&wat_pad=423,277)

* **Use Repository Filter (Optional):** Go to **By Repository (Used)** tab → Select a repo → Add used actions

![Setting up Actions Policy by Repository using Actions](https://ajeuwbhvhr.cloudimg.io/https://colony-recorder.s3.amazonaws.com/files/2025-05-05/6f6b7c07-313a-4ceb-8362-bd3a60dfc627/ascreenshot.jpeg?tl_px=272,142\&br_px=3024,1681\&force_format=jpeg\&q=100\&width=1120.0\&wat=1\&wat_opacity=1\&wat_gravity=northwest\&wat_url=https://colony-recorder.s3.amazonaws.com/images/watermarks/8B5CF6_standard.png\&wat_pad=863,276)

**Step 4: (Optional) Customize the PR Comment Template**

The editor includes a PR Comment Template used when this policy blocks a run from a pull request. Leave the default unchanged to use the standard StepSecurity comment, or customize it. See [PR Comment Template](#pr-comment-template) for the default template and supported placeholders.

**Step 5: Click "Save"**

<figure><img src="/files/LH1RhSudhVQGEKzFGcPW" alt=""><figcaption></figcaption></figure>

Follow this interactive demo to see how this workflow run policy works in practice:

{% embed url="<https://app.storylane.io/share/oyniugodihnf>" %}

### Runner Label Policy

Use this policy to block untrusted GitHub-hosted runners or allow only specific self-hosted runners.

#### Why It Matters

The `runs-on` value determines what machine a job actually executes on, and different runner labels have very different security and cost profiles.

The Runner Label Policy lets you enforce that jobs only execute on approved runner labels. This is useful when you want to block specific runner types, for example, preventing the use of GitHub-hosted runners because your organization standardizes on self-hosted runners for security or compliance reasons. With the allow list mode, you can go further and pin down exactly which labels and runner constraint values are permitted, including the structured `runs-on` strings used by third-party runner providers.

#### Creating a Runner Label Policy

**Step 1: Fill in Policy Details**

* Policy Name – e.g., *Do not allow GitHub-Hosted Runners*
* Policy Type – Select Runner Label Policy
* Action – Choose between:
  * Enforce: Actively blocks disallowed runner labels
  * Dry Run: Does not block the workflow run but records the violation in the Evaluations page

**Step 2: Choose a restriction mode**

The policy operates in one of two modes:

* **Disallowed labels (block list)**: blocks workflows that use any label on the list; everything else is allowed.
* **Allowed labels & constraints (allow list)**: only labels and constraint values on the list may be used. Each configured dimension is enforced independently, and a run that uses any label or constraint value outside the list violates the policy.

<figure><img src="/files/wrwx61fXudU2F5gQG8a1" alt=""><figcaption></figcaption></figure>

**Step 3 (Disallowed mode): Specify disallowed runner labels**

You can disallow runner labels in two ways, and combine both in the same policy:

* **Generic label groups**: check **GitHub-hosted standard runners** to disallow every GitHub-hosted standard runner label (Ubuntu, Windows, and macOS, covering latest, versioned, ARM, and preview labels) in one step. The group excludes larger runners and self-hosted runners. Click the label count next to the group name to see exactly which labels it represents. StepSecurity keeps this list up to date automatically as GitHub adds or retires labels, so you do not need to edit the policy when new runner images ship.
* **Custom labels**: type a runner label (e.g., `ubuntu-latest`, `my-self-hosted-runner`) and press Enter to add it. Use this for individual labels or labels not covered by a group.

Workflows using any disallowed runner label will be blocked by this policy.

**Step 3 (Allowed mode): Specify allowed labels and constraints**

In allowed mode, checking a generic label group allows all of its runner labels. Beyond groups, you configure two things:

* **Allowed labels**: plain runner labels (no `=`) that this policy permits, such as `ubuntu-latest` or `self-hosted`. You can also paste an entire `runs-on` string here to allow it verbatim. Leave the field empty to not restrict plain labels.
* **Runner constraints**: allowed values for each structured `runs-on` key, following the runs-on.com scheme (`family`, `cpu`, `image`, `ami`, `volume`, and so on). This is designed for teams using third-party runner providers such as RunsOn, which provision self-hosted machines through structured `runs-on` strings like:

```yaml
runs-on: runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache
```

Add one constraint row per key with all the values you want to allow. For example, a `cpu` row with `2`, `4`, and `8` allows any of those values. Only the keys you configure are enforced; any other `key=value` dimension a workflow uses is left unrestricted.

A few matching rules to be aware of:

* For a combined value like `cpu=2+8`, add it either as the single value `2+8` (order does not matter, so `8+2` matches too) or as the separate values `2` and `8`.
* Expression values are matched by their exact text. For example, a `runs-on` row with the value `${{ github.run_id }}` requires the routing label to use exactly that expression. An expression the policy does not list, such as `family=${{ vars.RUNNER_FAMILY }}`, violates the policy.

<figure><img src="/files/IF1lsDyiDJwZbrjsoXke" alt=""><figcaption></figcaption></figure>

**Step 4: (Optional) Customize the PR Comment Template**

The editor includes a PR Comment Template used when this policy blocks a run from a pull request. Leave the default unchanged to use the standard StepSecurity comment, or customize it. See [PR Comment Template](#pr-comment-template) for the default template and supported placeholders.

**Step 5: Select Repositories/Organizations**

Choose whether to apply the policy to:

* All current and future repositories/organizations *(default)*, or
* Select specific repositories/organizations manually

<figure><img src="/files/k4q830EVGr7wvLkiFrU2" alt=""><figcaption><p>Select Repositories</p></figcaption></figure>

**Step 6: Save the Policy**

* After configuring all settings, click Save to create the policy.

![Click the save button](https://ajeuwbhvhr.cloudimg.io/https://colony-recorder.s3.amazonaws.com/files/2025-05-05/83faa3ac-1ad1-4d61-8a12-95ced8eee9d3/ascreenshot.jpeg?tl_px=195,859\&br_px=1724,1714\&force_format=jpeg\&q=100\&width=1120.0\&wat=1\&wat_opacity=1\&wat_gravity=northwest\&wat_url=https://colony-recorder.s3.amazonaws.com/images/watermarks/8B5CF6_standard.png\&wat_pad=524,494)

Follow this interactive demo to see how this workflow run policy works in practice:

{% embed url="<https://app.storylane.io/share/z0ld7pz9bhmn>" %}

### Harden-Runner Policy

The Harden-Runner Policy ensures that every workflow run in your organization executes in a hardened environment. It blocks jobs where the [Harden-Runner](https://github.com/step-security/harden-runner) action is missing, or where it is present but is not configured as the first step of the job.

#### Why It Matters

Harden-Runner provides runtime security for GitHub-hosted runners — network egress filtering, file integrity monitoring, process monitoring, and outbound call auditing. These protections only take effect if the action is initialized **before** any other step runs. A job that runs `actions/checkout` or installs dependencies before Harden-Runner has already given any malicious code an unmonitored window to execute.

This policy reduces risk by:

* Guaranteeing that Harden-Runner is adopted consistently across all covered repositories
* Preventing developers from accidentally (or intentionally) removing Harden-Runner from a workflow
* Ensuring Harden-Runner is always the first step, so no code executes before monitoring is active
* Supporting organization-specific bootstrap actions as valid Harden-Runner equivalents

#### Creating a Harden-Runner Policy

**Step 1: Fill in Policy Details**

* **Policy Name** — e.g., `Require Harden-Runner`
* **Policy Type** — select **Harden-Runner Policy**
* **Action** — choose between:
  * **Enforce**: actively blocks jobs that are missing Harden-Runner or have it configured incorrectly
  * **Dry Run**: does not block the workflow run but records the violation on the Evaluations page

<figure><img src="/files/DX64jloDzGmPW3C245wJ" alt=""><figcaption></figcaption></figure>

**Step 2: (Optional) Scope the policy with Target runner labels**

By default the policy applies to **all jobs** across the selected repositories, including self-hosted runners that may not have the Harden-Runner action available.

To restrict the policy to specific runner types, toggle **Target runner labels** on, then select the labels the policy should apply to:

* **Generic label groups**: check **GitHub-hosted standard runners** to target every GitHub-hosted standard runner label (Ubuntu, Windows, and macOS, covering latest, versioned, ARM, and preview labels) in one step. The group excludes larger runners and self-hosted runners, and StepSecurity keeps its label list up to date automatically.
* **Custom labels**: type a label (e.g., `ubuntu-latest`) and press Enter to add it.

The policy will then only evaluate jobs whose `runs-on` value matches one of the selected labels.

{% hint style="info" %}
Use this when you have a mix of GitHub-hosted and self-hosted runners and only want to enforce Harden-Runner on GitHub-hosted ones.
{% endhint %}

**Step 3: (Optional) Add Custom Actions**

If your organization wraps Harden-Runner inside an internal bootstrap action, add those actions under **Custom Actions** so they are recognized as valid Harden-Runner equivalents.

* Type the action's `owner/repo` (for example, `my-org/bootstrap-security`) and press **Enter** to add it.
* Version tags are ignored — matching is done on the action name only.

**Step 4: Select Repositories/Organizations**

Choose whether to apply the policy to:

* All current and future repositories/organizations *(default)*, or
* Specific repositories/organizations selected manually

<figure><img src="/files/M9jH834o3yxMHE8QKP81" alt=""><figcaption></figcaption></figure>

**Step 5: Save the Policy**

**Follow this interactive demo to see how this workflow run policy works in practice:**

{% embed url="<https://app.storylane.io/share/m2vkc9k7lekt>" %}

### PR Comment Template

When a policy blocks a run that originates from a pull request, StepSecurity posts a comment on the PR explaining the block. You can customize this comment per policy in the policy editor, or leave the default unchanged (or reset it) to use the standard StepSecurity comment:

```
## {{policy_type}} Violation

[This workflow run]({{workflow_run_url}}) was blocked by the **{{policy_name}}** run policy.

{{policy_details}}
{{remediation}}

For more information, see [StepSecurity's documentation]({{docs_url}}).
```

The template supports the placeholders `{{workflow_run_url}}`, `{{policy_type}}`, `{{policy_name}}`, `{{policy_details}}`, `{{remediation}}`, `{{actor}}`, `{{owner}}`, `{{repo}}`, and `{{docs_url}}`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.stepsecurity.io/github-actions/workflow-run-policies/policies.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
