---
title: "The Free GitHub Action Demanded a License Mid-Project, So I Called the Binary Directly"
canonical: https://dxdev.com/blog/2026-08-22_the-free-security-scanner-that-started-charging/
datePublished: 2026-08-22
---
gitleaks-action@v2 now requires a paid `GITLEAKS_LICENSE`, even on private org repos. Same scanner, free path, once the action is out of the way.

The same commit also covered a Semgrep SARIF upload that was returning `403 Resource not accessible by integration`. `codeql-action/upload-sarif` needed to call the workflow-runs API, and adding `actions: read` was the fix.

Then the Gitleaks action wanted a paid license.

Those were not the same kind of CI break. The SARIF upload needed a missing permission. The secret scanner's wrapper now requires a paid license. Treating both as generic workflow failures would have made the repair worse.

## The failure was above the scanner

Semgrep left a useful trail. The SARIF upload was failing with `Resource not accessible by integration`, and the commit added `actions: read`.

That is the kind of break a wrapper action should stay for.

Gitleaks was different. `gitleaks-action@v2` now requires a paid `GITLEAKS_LICENSE`, including on private org repos.

| Layer | What we observed | What it meant |
| --- | --- | --- |
| SARIF upload | `403 Resource not accessible by integration` | The workflow needed `actions: read` to query workflow runs. |
| Secret scan wrapper | `gitleaks-action@v2` requires a paid `GITLEAKS_LICENSE` | The upstream binary was the free path. |

The diagnostic path was to split those layers apart. One failure belonged to the platform permission boundary. The other belonged to packaging around the underlying scanner.

## Keep the scanner, remove the toll booth

The commit comment says running the upstream binary keeps us on the free path until we choose to buy in.

Paying for the license was the other route.

We went with the binary. The workflow now runs Gitleaks `v8.21.2` directly. Same scanner, free path.

An action is not the tool it invokes. It is a layer of release packaging, defaults, permissions, and sometimes commercial policy. That layer is useful until it becomes the failure mode.

Direct binary invocation has costs. The workflow now carries the version pin and the download step, and moving past `v8.21.2` is a deliberate bump. In this case, that is the right trade. The dependency we care about is Gitleaks, not a wrapper that can change the operating terms underneath it.

## The smallest patch was not the smallest diff

The final change touched two workflow files: 11 inserted lines and 7 deletions. One added `actions: read` so SARIF upload could reach the workflow-runs API. The other replaced `gitleaks-action@v2` with a direct call to Gitleaks `v8.21.2`.

Those changes look similar in a commit summary. They were not similar decisions. For Semgrep, the commit added `actions: read`. For Gitleaks, it replaced the action with the binary call.

When a CI action breaks, I now start with a narrower question than “How do I fix the action?” I ask what actually failed: the scanner, the platform permission, or the packaging around the scanner.

In this case, the answer was the packaging. We kept the scan and removed the toll booth.
