Skip to content

carabiner v0.1.13

Choose a tag to compare

@MaXiMo000 MaXiMo000 released this 22 Aug 21:22
· 19 commits to main since this release

Make any repo secure by default, keep it that way, and prove the controls fire.

- uses: MaXiMo000/carabiner@v0.1.13
pip install carabiner-sec        # installs the `carabiner` command
docker run --rm -v "$PWD:/repo:ro" ghcr.io/maximo000/carabiner:0.1.13 scan --all

Calibrated against 60 public repositories

Tuned by scanning 60 real projects — tokio, grafana, vault, rails, next.js, home-assistant, airflow and more — across 13 languages, then re-checking every finding with an oracle written independently of the engines.

The default output is now a median of 9 findings per repository. Roughly 800 informational ones across the corpus are counted but not listed until you pass --info. Hidden never means disappeared: the count always shows.

A version tag on an action is informational. A moving branch in someone else's repository is not — that is what can change tonight without a diff on your side. Classifying refs by shape rather than by a list of branch names also caught stable, nightly and cargo-hack. And one unpinned reference is one finding, however many steps use it: tokio reaches dtolnay/rust-toolchain@stable 34 times in a single workflow, which is one decision, not 34 lines.

tokio went from 172 findings to 10. Corpus criticals went from 31 to 2 — and both survivors are real: a committed private key in grafana, and an unguarded pull_request_target + PR-head checkout in rollup.

New: a native Dockerfile engine

Trivy scans a built image — daemon, build, minutes. A Dockerfile is text, and the mistakes that matter most are visible before anything is built.

DOCK001 the final stage never drops root
DOCK002 untagged or :latest base image
DOCK003 a credential baked into ARG/ENV, readable with docker history
DOCK004 a remote script piped straight into a shell at build time
DOCK005 TLS verification disabled during the build

54 real issues across 19 of the 60 repositories — but only after spot-checking killed 33 of the rule's first 36 findings. FROM builder is a stage reference, scratch is a keyword, ${BASE_IMAGE} is unknowable, and airflow embeds entire Python programs in BuildKit heredocs that were being parsed as instructions.

Fixed: findings that named an action or image were unreadable

The redactor treated /, . and - as token characters, so dtolnay/rust-toolchain@stable printed as dtol...hain@stable. A credential is a long unbroken run with no separators to split on — splitting on them keeps identifiers legible while still masking ghp_… and AKIA… tokens.

What it does

A ratchet. carabiner init accepts your existing findings into a baseline, so CI goes green today and only new problems fail it. --expires 90 puts a deadline on accepted debt.

A drill. carabiner drill doesn't read config — it plants a private key and checks your hooks actually block it, then asks GitHub whether push protection is really on. A check that could not run never reports as passing.

One normalized model → SARIF into the PR's Security tab, deduplicated across engines, most-severe-wins.

52 tests, 193 checks, on Linux and Windows across Python 3.10 and 3.13, plus a Docker build, a wheel install into a clean venv, and carabiner scanning itself — every commit.