Skip to content
crenoxhqPublic

About

Statically compiled, zero-dependency Git pre-commit secret scanner and credentials detector written in Go. An alternative to Gitleaks and git-secrets.

Topics

Resources

Contributing

Security policy

Stars

31 stars

Watchers

2 watching

Forks

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

Crenox — Statically Compiled Git Secret Scanner & Pre-Commit Hook

 ██████╗ ██████╗  ███████╗ ████╗  ██╗  ██████╗  ██╗  ██╗
██╔════╝ ██╔══██╗ ██╔════╝ ██╔██╗ ██║ ██╔═══██╗ ╚██╗██╔╝
██║      ██████╔╝ █████╗   ██║╚██╗██║ ██║   ██║  ╚███╔╝ 
██║      ██╔══██╗ ██╔══╝   ██║ ╚████║ ██║   ██║  ██╔██╗ 
╚██████╗ ██║  ██║ ███████╗ ██║  ╚███║ ╚██████╔╝ ██╔╝╚██╗
 ╚═════╝ ╚═╝  ╚═╝ ╚══════╝ ╚═╝   ╚══╝  ╚═════╝  ╚═╝  ╚═╝

Statically compiled Git pre-commit secret scanner and credentials detector written in Go.

Secured by Crenox

Release Go Reference GitHub Marketplace Downloads

OpenSSF Best Practices CI License Go Version

Platforms Mentioned in Awesome Go Stars



Crenox Secret Scanner - Context-aware Git secret scanner in Go | Product Hunt


What is Crenox?

Crenox is a statically compiled, zero-dependency Git pre-commit hook and credentials scanner written in Go. It automatically blocks commits of API keys, SSH private keys, cloud credentials, database connection strings, and other sensitive material before they are recorded in version control history.

It is an alternative to tools like Gitleaks, TruffleHog, and git-secrets, focusing on lower resource utilization and local workflow integration.

Crenox uses a three-tier detection pipeline designed for speed and false positive reduction:

Tier Engine Purpose
1 — PATTERN Aho-Corasick automaton Matches 135+ known secret signatures in O(n) time, zero allocations
2 — ENTROPY Shannon entropy analysis Catches unknown secrets by measuring information density (with Log2 LUT acceleration)
3 — CONTEXT Context classifier Suppresses false positives from comments, test files, container digests, and placeholders

A finding must survive all three tiers before it is reported.


Quick Start

Install and protect your repository in under 60 seconds:

# 1. Automatic One-Line Install (Linux, macOS, Termux)
curl -fsSL https://crenoxhq.github.io/crenox/install.sh | bash

# Or via Go:
# go install github.com/crenoxhq/crenox/v2/cmd/crenox@latest

# 2. Protect current repository (if skipped during installer)
crenox install

# 3. Verify — Crenox will now scan every commit automatically
git add . && git commit -m "test"

Or scan any directory right now, without a hook:

crenox scan --recursive ./src

That is all. No configuration file required. No runtime dependencies. Works on Linux, macOS, Windows, and Android/Termux.


Crenox Pro

For engineering teams and organizations, Crenox Pro provides centralized policy management, audit logging, and fleet-wide pre-commit enforcement within private infrastructure.

https://crenoxhq.github.io/crenox/crenox-demo.mp4

Enterprise Specification & Pre-Order → • Watch Demonstration (MP4)


Terminal Demo

asciinema play https://crenoxhq.github.io/crenox/demo.cast

Crenox Demo


Table of Contents


Performance

Measured on real-world repositories with Crenox against the most popular alternatives, on a standard GitHub Actions Ubuntu cloud runner — the same infrastructure used in real-world CI/CD pipelines.

Filesystem Scan Results (Standard Mode Averages)
Repository Tool Avg Scan Time (s) Avg CPU Time (s) Peak RAM (MB) Findings
sample_secrets Crenox 0.0072 s 0.0095 s 17.7 MB 4
Gitleaks v8.18.2 0.0241 s 0.0307 s 22.7 MB 1
Betterleaks v1.6.1 0.0944 s 0.1219 s 45.4 MB 2
truffleHogRegexes Crenox 0.0073 s 0.0105 s 18.0 MB 0
Gitleaks v8.18.2 0.0372 s 0.0498 s 21.2 MB 1
Betterleaks v1.6.1 0.1189 s 0.1613 s 48.5 MB 1
serverless-node-api-boilerplate Crenox 0.0074 s 0.0109 s 18.1 MB 6
Gitleaks v8.18.2 0.0271 s 0.0393 s 23.0 MB 2
Betterleaks v1.6.1 0.2510 s 0.4711 s 66.1 MB 2
large_file_test (10 MB) Crenox 0.0111 s 0.0138 s 18.0 MB 0
Gitleaks v8.18.2 1.8080 s 1.8326 s 23.8 MB 1
Betterleaks v1.6.1 0.7533 s 0.8140 s 73.0 MB 2
Git History Scan Results (History Mode Averages)
Repository Tool Avg Scan Time (s) Avg CPU Time (s) Peak RAM (MB) Findings
sample_secrets Crenox 0.0096 s 0.0136 s 18.0 MB 9
Gitleaks v8.18.2 0.0285 s 0.0384 s 23.1 MB 5
Betterleaks v1.6.1 0.2931 s 0.5716 s 67.5 MB 6
truffleHogRegexes Crenox 0.0131 s 0.0226 s 18.4 MB 3
Gitleaks v8.18.2 0.0425 s 0.0595 s 23.6 MB 6
Betterleaks v1.6.1 0.1309 s 0.2237 s 49.8 MB 8
serverless-node-api-boilerplate Crenox 0.0105 s 0.0159 s 18.1 MB 6
Gitleaks v8.18.2 0.0306 s 0.0463 s 21.4 MB 2
Betterleaks v1.6.1 0.2457 s 0.4333 s 66.7 MB 2
large_file_test (10 MB) Crenox 0.0049 s 0.0051 s 13.6 MB 0
Gitleaks v8.18.2 0.0236 s 0.0299 s 23.2 MB 0
Betterleaks v1.6.1 0.0320 s 0.0394 s 30.4 MB 0

Transparent & Auditable — These results were generated by an open-source automated benchmark script running on a standard GitHub Actions Ubuntu cloud runner. The full methodology, tooling, and workflow configuration are publicly visible — results are triggered and verified by the maintainer on each release. View the latest benchmark run →

Summary:

Metric vs Gitleaks vs Betterleaks
Speed 2.9x to 162x faster 6.5x to 67x faster
Memory 1.2x to 1.7x less RAM 2.2x to 4.1x less RAM
Recall (Accuracy) Finds 4x more secrets on sample_secrets (4 vs 1) Finds critical multi-file secrets missed by Betterleaks

Why Crenox — Comparison (vs Gitleaks, TruffleHog, detect-secrets, Betterleaks)

Feature Crenox Gitleaks TruffleHog detect-secrets Betterleaks
Statically compiled, zero runtime dependencies + + + — (Python) +
ARM / Android / Termux native support + partial partial partial partial
Aho-Corasick O(n) multi-pattern matching + + + — +
Shannon entropy analysis + + + + —
Token efficiency (BPE tokenizer) — — — — +
Context-aware false-positive suppression + — — partial —
BIP-39 seed phrase detection + — — — —
Single-layer Base64 decoding + + + — +
Concurrent file scanning + + + + +
SARIF & GitLab SAST output formatting + + — — +
Global hook installation + + — — —
Custom user-defined signatures + + + + +
OTA self-updating binary + — + — —

Architecture

Detection Pipeline

  git commit (staged changes)
         |
  [Git Interop — internal/git]
   ListStagedFiles()   →  git diff --cached --name-status
   GetStagedDiff()     →  git diff --cached -- <path>  (modified files, added lines only)
   GetStagedContent()  →  git show :<path>              (new files, full content)
         |
  [Pre-flight Filters]
   - Bounded streaming queue (size 1024) to keep memory footprint bounded under concurrent load
   - Early directory pruning: walks ignore excluded paths (e.g. node_modules, vendor) on the fly
   - Binary detection: null-byte scan of first 8 KB (isBinaryFileFast)
   - Extension exclusion: case-insensitive match
   - Path exclusion: filepath.Match glob patterns
   - File size cap: files > 10 MB skipped
         |
  [Tier 1 — Aho-Corasick Trie  —  internal/trie/trie.go]
   Built once at startup via trie.Build() — allocation-free DFA matching of 135+ signatures
   Case-insensitive O(n) scan using a branchless 256-byte toLower lookup table
   and sync.Pool-recycled 64 KB streaming buffers to cap heap memory to ~3 MB
   BIP-39 mnemonic detection: 12/15/18/21/24 words validated against 2048-word dictionary
   Single-layer Base64 decoding: re-feeds decoded value through trie (catches K8s secrets)
   Blob aggregation: 3+ consecutive high-entropy lines → single CRITICAL finding
         |
  [Tier 2 — Shannon Entropy  —  internal/entropy/entropy.go]
   Precomputed Log2 Look-Up Table (LUT) for O(1) symbol entropy arithmetic
   Single-pass candidate token extractor for Base64 and Hex character sets
   Base64 tokens: entropy >= entropy_threshold (default 4.5 bits/symbol)
   Hex tokens:    entropy >= threshold × (4.0 / 6.0), min floor 3.0
   Skips: tokens < min_secret_length (default 20), all-identical chars, Java-style identifiers
         |
  [Tier 3 — Context Filter  —  internal/context/context.go]
   Checks (in order): test file path → commented line → unquoted RHS in source → UUID → version string
                    → env var placeholder → config placeholder → variable name → short alpha token
   Only Real decisions are forwarded to the reporter
         |
  [Reporter  —  internal/reporter/reporter.go]
   pretty / plain         → stderr    (human-readable, ANSI color)
   json / sarif / gitlab  → stdout    (or directly to file if --output is used)
         |
  exit 0 (CLEAN)  |  exit 1 (BLOCKED)
Tier 1 — Aho-Corasick Pattern Matching (detailed)

Source: internal/trie/trie.go

The automaton is built once at startup via trie.Build(sigs) and shared across all goroutines without locks. Automaton.Search performs zero heap allocations on the hot path.

Construction is two-phase:

  1. All signature prefixes are inserted into a trie with lowercased keys.
  2. BFS computes failure links and merges output sets so overlapping prefixes (e.g. sk- and sk-proj-) are both reported in one pass.

The scanner applies additional logic on top of the raw trie results:

  • BIP-39 mnemonics — line is tested for 12/15/18/21/24 space-separated words, all validated against bip39.go.
  • Single-layer Base64 decoding — extracted values are decoded and re-fed through the trie to catch masked secrets (e.g. Kubernetes Secret manifests).
  • Blob aggregation — 3+ consecutive lines of the same entropy class are collapsed into one CRITICAL finding (massive-base64-blob / massive-hex-blob) to prevent alert fatigue.
  • Same-Line & Cross-Signature Deduplication — preserves distinct secrets occurring on the same line without overwriting, while upgrading lower-priority matches when a higher-severity signature covers the exact same token.
  • Tier 1 Precedence over Entropy — tokens already matched by specific Tier 1 pattern rules suppress redundant Tier 2 Shannon entropy alerts on the same line.
Tier 2 — Shannon Entropy Analysis (detailed)

Source: internal/entropy/entropy.go

H(X) = - sum over i of P(xi) * log2(P(xi))
Score Meaning
0.0 All bytes identical
~3.5 English prose
~5.5 – 6.5 Cryptographically random Base64 secret
8.0 Perfectly uniform 256-symbol distribution

Crenox extracts two token classes per line:

  • Base64 tokens — runs of A-Za-z0-9+/=_-; entropy must exceed entropy_threshold (default 4.5).
  • Hex tokens — runs of 0-9a-fA-F; must be even-length if short (< 32 characters) to filter out arbitrary non-secret hex strings; threshold is scaled: entropy_threshold × (4.0 / 6.0), floor 3.0.

Pre-filters applied before entropy computation: Java-style identifiers (all letters/dots/underscores) and all-identical-character tokens are discarded.

Tier 3 — Context-Aware Filtering (detailed)

Source: internal/context/context.go

Decision Condition
Real None of the suppression checks matched
SafeComment Line begins with // # * /* <!-- -- ; % !
SafeTestFile Path ends with _test.go _spec.rb .test.js .spec.ts .md .rst _test.py conftest.py, starts with test_ (.py), begins with test-, or contains directory: test tests testdata fixtures __tests__ __mocks__ mock mocks sample samples docs doc
SafeVariableName Variable name (left of = / :=) contains: dummy fake mock placeholder sample fixture stub lorem foobar your_ your- insert_ replace_ changeme redacted sanitized censored, or variable name matches assigned value (self-assignment)
SafePlaceholder Token matches $VAR, ${VAR}, <...>, [[...]], {{...}}, ${{...}}, redacted Bearer values (Bearer <redacted>), framework schema casts (`nullable
SafeUUID Token matches UUID v4 pattern xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
SafeVersionString Token begins with digit.digit.digit or consists of pure numeric sequences
SafeSourceRHS Token on RHS of assignment in programming source files is bare/unquoted (e.g. Go struct types, function calls)

The scanner also rejects tokens that match a printf-style format verb, are identical to their signature prefix, contain regex metacharacters, or (for short prefixes ≤ 3 bytes) are pure PascalCase/CamelCase with no special characters.

Inline Suppression

Place crenox:ignore on the flagged line or the preceding comment line:

// crenox:ignore
apiKey := "sk_live_example_for_documentation"

apiKey := "sk_live_example_for_documentation" // crenox:ignore
# crenox:ignore
STRIPE_KEY="sk_live_example"
<!-- crenox:ignore -->
<secret>sk-ant-api03-documented-example</secret>

A same-line annotation suppresses only that line. A comment-line annotation suppresses the immediately following line.


Signature Coverage

View all builtin signatures
Category Signatures
AI & ML Platforms OpenAI (sk-, sk-proj-), DeepSeek (sk-), Anthropic (sk-ant-), OpenRouter (sk-or-v1-), Cohere (co_), Together AI (tog_), Mistral AI (mis_), Groq (gsk_), Replicate (r8_), Resend (re_), Perplexity (pplx-), Fireworks AI (fw_), LangSmith (lsv2_), HuggingFace (hf_)
Cloud Databases & Services Alibaba Cloud AccessKey ID (LTAI), Azure Storage Account Key, Doppler (dp.st., dp.pt., dp.sa.), Supabase (sb_publishable_, sb_secret_), Turso (fn_), Tailscale (tskey-auth-, tskey-api-), Clerk (clerk_), Neon (npg_), PlanetScale (pscale_pw_, pscale_tkn_), Terraform Cloud (atlasv1)
DevSecOps & Analytics Grafana Cloud Token (glc_), Infracost API Key (ico-), Datadog (ddp_), PostHog (phx_, phs_, pha_), Linear (lin_api_), Sentry (sntry_), SonarQube (squ_), Snyk (snyk_), Pulumi (pul-), Databricks (dapi), Svix Webhook (whsec_)
Social & Messaging Telegram Bot API Token (bot<id>:<hash>), Discord Webhook (https://discord.com/api/webhooks/), Slack Bot (xoxb-), User (xoxp-), Workspace (xoxa-), Webhook (https://hooks.slack.com/services/)
GitHub Classic PAT (ghp_), OAuth (gho_), User-to-Server (ghu_), App Installation (ghs_ - classic & JWT), Refresh (ghr_), Fine-grained PAT (github_pat_), Environment tokens (GITHUB_TOKEN, _GITHUB_TOKEN)
Heroku API Key (HEROKU_API_KEY), OAuth Token (heroku_oauth_token)
GitLab Personal Access Token (glpat-), Pipeline Trigger (glptt-), Runner Registration (GR1348941), Runner Token (glrt-)
Postman API Key (PMAK-)
AWS Access Key ID (AKIA), MFA Device (ABIA), STS Temporary Key (ASIA), AppSync (da2-), Secret Access Key variable assignments (aws_secret, aws_key)
Google Cloud Service Account JSON ("type": "service_account"), API Key (AIzaSy), OAuth Client ID (.apps.googleusercontent.com), OAuth Client Secret (GOCSPX-)
Stripe Live Secret (sk_live_), Live Restricted (rk_live_), Test Secret (sk_test_)
Twilio & SendGrid Account SID (AC), Auth Token (SK), SendGrid (SG.), Mailgun (key-)
npm & PyPI npm Automation token (npm_), Auth Token (_authToken=, _auth=), PyPI Upload Token (pypi-)
JWT JSON Web Token (eyJ, strict 3-part dot-separated regex)
Private Keys & Certs Age Secret Keys (AGE-SECRET-KEY-1), RSA, EC, OpenSSH, PKCS#8, PGP, DSA — all -----BEGIN ... PRIVATE KEY----- variants, PuTTY Private Keys (PuTTY-User-Key-File-)
Databases & DSNs PostgreSQL (postgresql://, postgres://), MySQL (mysql://), MongoDB SRV (mongodb+srv://), MongoDB (mongodb://), Redis (redis://), RabbitMQ (amqp://, amqps://)
Basic Auth HTTPS (https://user:pass@), HTTP (http://user:pass@)
Vault & DigitalOcean HashiCorp Vault Service (hvs.), Batch (hvb.), DigitalOcean (dop_v1_), Vercel (vercel_), Cloudflare (cloudflare-api-token), HuggingFace (hf_), Shopify (shpat_, shpca_, shppa_)
Generic & Frameworks password=, secret=, api_key=, token=, auth=, SECRET_KEY =, Rails secret_key_base, WordPress keys (AUTH_KEY, SECURE_AUTH_KEY, etc.), Python Descriptor Filters (ConfigAttribute[...])
Crypto Wallets BIP-39 mnemonic (12/15/18/21/24 words, validated against 2048-word dictionary)

Custom signatures can be added in .crenox.yaml and are compiled into the same automaton at startup — no performance overhead.


Show Your Security Badge

Show that your repository is actively protected against hardcoded secrets by embedding the official Secured by Crenox badge in your README.md:

Option 1 — Platinum Silver Shield

Secured by Crenox

[![Secured by Crenox](https://img.shields.io/badge/Secured%20by-Crenox-E2E8F0?style=for-the-badge&logo=shield&logoColor=0F172A&labelColor=0F172A)](https://github.com/crenoxhq/crenox)

Option 2 — Emerald Minimalist

Protected by Crenox

[![Protected by Crenox](https://img.shields.io/badge/Protected%20by-Crenox-10B981?style=flat-square&logo=git&logoColor=white)](https://github.com/crenoxhq/crenox)

Option 3 — DevSecOps Capsule

Crenox Secret Scanner

[![Crenox Secret Scanner](https://img.shields.io/badge/Secret%20Scanner-Crenox-3c6382?style=flat-square&logo=githubactions&logoColor=white)](https://github.com/crenoxhq/crenox)

Installation

Homebrew (macOS & Linux)

Install Crenox via the official Homebrew tap:

brew tap crenoxhq/crenox
brew install crenox

Or install directly in a single command:

brew install crenoxhq/crenox/crenox

Termux / Android (TUR)

Install Crenox natively on Android via the Termux User Repository (TUR):

pkg install tur-repo
pkg install crenox

One-Line Automatic Installer (Linux, macOS, Termux)

Install Crenox with automatic platform architecture detection and SHA-256 integrity verification:

curl -fsSL https://crenoxhq.github.io/crenox/install.sh | bash

Security & Reliability:

  • Truncation Protection: Wrapped inside a syntax block to prevent partial execution if the network connection drops.
  • Integrity Verification: Automatically checks SHA-256 cryptographic checksums against signed release metadata before execution.
  • Zero Root Requirement: Automatically falls back to $HOME/.local/bin without failure if sudo is unavailable.
  • Termux / Android Support: Automatically detects Termux environments and deploys directly to $PREFIX/bin.
  • Interactive /dev/tty: Supports interactive hook selection even when piped from curl.

Automation & Non-Interactive Flags:

# Install binary & automatically protect current Git repository:
curl -fsSL https://crenoxhq.github.io/crenox/install.sh | bash -s -- --local

# Install binary & protect ALL Git repositories globally:
curl -fsSL https://crenoxhq.github.io/crenox/install.sh | bash -s -- --global

# Install binary only (skip Git hook setup):
curl -fsSL https://crenoxhq.github.io/crenox/install.sh | bash -s -- --no-hook

# Pin to a specific version or custom directory:
curl -fsSL https://crenoxhq.github.io/crenox/install.sh | bash -s -- --version=v2.1.11 --dir=/usr/local/bin

Go Toolchain

go install github.com/crenoxhq/crenox/v2/cmd/crenox@latest

Pre-compiled Standalone Binaries (Manual Download)

Download the binary for your platform from the GitHub Releases page:

# Linux / macOS (replace <version> and <arch>, e.g. linux-amd64, darwin-arm64):
wget https://github.com/crenoxhq/crenox/releases/download/<version>/crenox-<version>-<arch> -O crenox
chmod +x crenox
sudo mv crenox /usr/local/bin/

# Android (Termux):
wget https://github.com/crenoxhq/crenox/releases/download/<version>/crenox-<version>-android-arm64 -O $PREFIX/bin/crenox
chmod +x $PREFIX/bin/crenox

# Verify installation:
crenox version

Native pre-commit Framework

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/crenoxhq/crenox
    rev: v2.1.11 # Replace with the latest release version
    hooks:
      - id: crenox

GitHub Actions CI/CD

- name: Run Crenox Secret Scanner
  uses: crenoxhq/crenox@latest
  with:
    args: "."

Build from Source

git clone https://github.com/crenoxhq/crenox.git
cd crenox
make build            # outputs to dist/crenox
./dist/crenox version

Git Hook Setup

Protect the current repository:

crenox install          # installs .git/hooks/pre-commit
crenox install --force  # overwrites an existing hook

Protect every repository on this machine (global):

crenox install --global
# Creates ~/.config/crenox/hooks/pre-commit
# Runs: git config --global core.hooksPath ~/.config/crenox/hooks

Remove global hook:

git config --global --unset core.hooksPath

Full uninstall — removes binary, hooks, and config directory:

crenox uninstall

Configuration

Crenox searches for .crenox.yaml in this order:

  1. --config / -c flag value
  2. .crenox.yaml in the scanned target directory or repository root
  3. .crenox.yaml in the current working directory
  4. ~/.crenox.yaml in the home directory

With no config file, built-in defaults apply. The file is merged on top of defaults, so omitted fields keep their default values.

Full configuration reference
# Shannon entropy threshold (bits/symbol). Range: 0.0 to 8.0.
# Raise to reduce false positives. Lower to increase sensitivity.
# Default: 4.5
entropy_threshold: 4.5

# Minimum token length for entropy analysis.
# Tokens shorter than this produce unreliable scores.
# Default: 20
min_secret_length: 20

# Maximum file size to scan. Files exceeding this are skipped.
# Default: 10485760 (10 MB)
max_file_size_bytes: 10485760

# Whether to scan binary files (detected by null-byte in first 8 KB).
# Default: false
scan_binary_files: false

# Glob patterns (relative to repo root) to skip entirely.
# Default list below:
exclude_paths:
  - "vendor/**"
  - "node_modules/**"
  - "dist/**"
  - "build/**"
  - "out/**"
  - "target/**"
  - "bin/**"
  - ".next/**"
  - ".nuxt/**"
  - ".yarn/**"
  - ".git/**"
  - "*.lock"
  - "go.sum"
  - "package-lock.json"
  - "pnpm-lock.yaml"
  - "yarn.lock"
  - "bun.lock"
  - "bun.lockb"
  - "**/locales/**"
  - "**/i18n/**"
  - "**/*.min.js"
  - "**/*.min.css"
  - "**/go/pkg/mod/**"
  - "**/var/lib/apt/**"
  - "**/var/lib/dpkg/**"
  - "**/*.ziphash"
  - "**/.env.example"
  - "**/.env.sample"

# File extensions to skip (case-insensitive).
# Default includes images, fonts, audio, video, archives, binaries, office documents.
exclude_extensions:
  - ".png"
  - ".jpg"
  - ".jpeg"
  - ".gif"
  - ".bmp"
  - ".ico"
  - ".svg"
  - ".woff"
  - ".woff2"
  - ".ttf"
  - ".eot"
  - ".mp4"
  - ".webm"
  - ".mp3"
  - ".ogg"
  - ".zip"
  - ".tar"
  - ".gz"
  - ".bz2"
  - ".xz"
  - ".7z"
  - ".pdf"
  - ".doc"
  - ".docx"
  - ".xls"
  - ".xlsx"
  - ".exe"
  - ".dll"
  - ".so"
  - ".dylib"
  - ".a"
  - ".o"
  - ".css"
  - ".scss"
  - ".csv"
  - ".hex"
  - ".eml"
  - ".msg"
  - ".mbox"
  - ".vcf"
  - ".ics"
  - ".cache"
  - ".pb.go"
  - ".gen.go"
  - ".g.go"
  - ".map"

# Allowlist: findings whose token matches are silently ignored.
# Supports exact strings and filepath.Match glob patterns.
allowlist_patterns:
  - "AKIAIOSFODNN7EXAMPLE"
  - "sk_test_*"
  - "*-dummy-token-*"

# Disable individual detection tiers. Use with caution.
disable_tiers:
  trie: false     # disables Tier 1 Aho-Corasick matching
  entropy: false  # disables Tier 2 entropy analysis
  context: false  # disables Tier 3 suppression — expect many false positives

# Stop after the first finding. Useful in CI fail-fast loops.
fail_fast: false

# Print debug output to stderr.
verbose: false

# Custom signatures compiled into the Aho-Corasick automaton alongside builtins.
# Regular expressions are safely validated during initialization (malformed regexes are skipped with a warning).
# Severity must be one of: CRITICAL, HIGH, MEDIUM, LOW  (defaults to HIGH if omitted).
custom_signatures:
  - id: "internal-api-key"
    description: "Proprietary internal service credential"
    prefix: "mycompany_key_"
    severity: "CRITICAL"
    regex: "^mycompany_key_[a-zA-Z0-9]{32}$"

Entropy Threshold Reference

Value Behavior
3.0 Very sensitive — may flag base32 identifiers
3.5 High sensitivity — catches most secrets, slightly elevated noise
4.5 Default — cryptographically random secrets, low false-positive rate
5.0+ Strict — may miss weak passwords; minimal noise

Usage

Automatic (Pre-commit Hook)

After crenox install, the hook fires on every git commit and scans only staged content:

  • New files — full content via git show :<path>
  • Modified files — added lines only via git diff --cached -- <path>

Ad-hoc Scanning

# Single file
crenox scan config/production.yaml

# Directory (non-recursive)
crenox scan ./config

# Directory, recursive (skips .git, build, node_modules automatically)
crenox scan -r ./src

# Full Git history audit (streams git log --all --full-history -p -m; scans file diffs, commit messages, and merge commits)
crenox scan --history .

# JSON output — written to stdout for piping
crenox scan -f json -r ./src | jq '.findings[] | select(.severity == "CRITICAL")'

# SARIF output saved directly to file (keeps pretty terminal logs)
crenox scan -f sarif -o crenox.sarif .

# GitLab Secret Detection SAST report
crenox scan -f gitlab-sast -o gl-secret-detection-report.json .

In both ad-hoc directory mode and full Git history mode, scans are processed concurrently using max(runtime.NumCPU(), 4) worker pools. Multi-file reporting guarantees that identical secrets appearing across separate files are each reported with their exact file path and line location. In history mode, Git log diffs and commit messages across all branches and merge commits are streamed with chunk deduplication by token value.

CI Integration

GitHub Actions (Official Reusable Action)

The easiest way to integrate Crenox into your GitHub Actions workflow is by using our official reusable action. It handles Go installation, compilation cache, and scanning automatically:

- name: Run Crenox Git Secrets Scanner
  uses: crenoxhq/crenox@v2
  with:
    version: 'latest' # Optional: specific version to use (e.g. 'v2.x.x')
    args: '.'         # Optional: arguments to pass (e.g. "." or "--history .")
    sarif: 'true'     # Optional: set to 'true' to export findings as a SARIF report

To upload the results to GitHub Advanced Security (Code Scanning Alerts), configure the upload step:

- name: Upload SARIF report
  if: always()
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: crenox-results.sarif

Tip

You can inspect the official action.yml file in the root of this repository to use as a template or reference for building your own custom GitHub Actions.

GitLab CI/CD (Secret Detection SAST)

crenox-secret-detection:
  stage: test
  image: golang:1.22
  before_script:
    - go install github.com/crenoxhq/crenox/v2/cmd/crenox@latest
  script:
    - crenox scan -f gitlab-sast -o gl-secret-detection-report.json .
  artifacts:
    reports:
      secret_detection: gl-secret-detection-report.json

Command Reference

crenox run — pre-commit hook entry point

Scans staged changes only. Invoked automatically by the Git hook. Enforces strict positional argument validation (cobra.NoArgs).

Zero-Tolerance Blocking Policy: Any detected finding, regardless of severity (CRITICAL, HIGH, MEDIUM, LOW), blocks the commit with exit code 1.

Fail-Closed Scan Integrity: Any unreadable file, inaccessible path, permission denial, or broken Git pipe halts execution and blocks the commit with exit code 1 (scan_error). A clean status (exit 0) is emitted only when 100% of eligible files have been scanned with zero findings.

Flag Default Description
-c, --config auto-detected Path to .crenox.yaml
-f, --format pretty pretty json plain sarif gitlab-sast
--fail-fast false Stop after the first finding
-v, --verbose false Debug output to stderr
crenox scan [path...] — ad-hoc scanner

Scans targeted files, directories, or historical Git commit logs. Enforces fail-closed scan integrity: if any file is unreadable or a history process fails, scanning halts with an incomplete scan report and exit code 1.

Flag Default Description
-c, --config auto-detected Path to .crenox.yaml
-f, --format pretty pretty json plain sarif gitlab-sast
-o, --output Write report directly to file, preserving pretty stdout logs
-r, --recursive false Walk subdirectories
--history false Scan entire Git commit history
--fail-fast false Stop after the first finding
-v, --verbose false Debug output to stderr
crenox install — hook installer

Installs the Crenox pre-commit hook into .git/hooks/pre-commit or globally. Enforces strict positional argument validation (cobra.NoArgs).

Flag Default Description
--global false Install globally via core.hooksPath
--repo . Target repository root
-f, --force false Overwrite existing hook
crenox update — OTA self-updater

Downloads the latest stable release for the current OS/arch from GitHub Releases, performs strict SHA-256 checksum verification, and atomically replaces the running executable. Enforces strict positional argument validation (cobra.NoArgs).

Integrity Verification: Checksums must be valid 64-character hexadecimal SHA-256 hashes (^[a-fA-F0-9]{64}$). If a checksum is missing, invalid, or does not match the binary, the update immediately aborts and cleans up temporary files.

Flag Default Description
--beta false Allow updating to pre-release (beta) versions
--skip-verify false Skip SHA-256 checksum verification (not recommended)

A background check runs on each invocation, querying the API at most once per 24 hours. The result is cached at ~/.config/crenox/last_check.json. A notice is printed to stderr if a newer version is available.


Output Reference

Output Examples

Clean (exit 0):

  CRENOX CLEAN  --  4 file(s) scanned in 3.2ms

Blocked by Finding (exit 1):

   CRITICAL   cmd/main.go:12
               [PATTERN] GitHub Personal Access Token (classic)
               Token:  ghp_AB****************************cdef
               -> token := "ghp_AB...cdef"

   HIGH       config/settings.go:8
               [ENTROPY] High-entropy BASE64 string (entropy=6.23)
               Token:  wJalrX****************************EY
               -> AWS_SECRET = "wJalrX...EY"

---------------------------------------------------------------------
  Files scanned : 4  |  Elapsed : 5.1ms
  CRITICAL:1   HIGH:1   MEDIUM:0   LOW:0
---------------------------------------------------------------------
  COMMIT BLOCKED -- remove the secrets above and try again.

Incomplete Scan / Fail-Closed (exit 1):

  CRENOX SCAN INCOMPLETE  --  1 unreadable file(s) encountered

  Unreadable files:
    - secret.key (open secret.key: permission denied)

  COMMIT BLOCKED -- resolve filesystem/Git errors and try again.

JSON Schema (-f json — written to stdout):

{
  "crenox_version": "v2.x.x",
  "status": "blocked",
  "scanned_files": 4,
  "elapsed_ms": 5,
  "findings": [
    {
      "file_path": "cmd/main.go",
      "line": 12,
      "severity": "CRITICAL",
      "tier": "PATTERN",
      "signature_id": "github-pat-classic",
      "description": "GitHub Personal Access Token (classic)",
      "token": "ghp_AB****************************cdef",
      "entropy": 5.23,
      "line_snippet": "token := \"ghp_AB...cdef\""
    }
  ]
}

When an unreadable file or pipeline failure triggers fail-closed protection, -f json emits:

{
  "crenox_version": "v2.x.x",
  "status": "scan_error",
  "scanned_files": 3,
  "elapsed_ms": 2,
  "failed_files": [
    {
      "path": "secret.key",
      "error": "open secret.key: permission denied"
    }
  ],
  "findings": []
}

SARIF (-f sarif — written to stdout): OASIS SARIF 2.1.0 JSON format, compatible with GitHub Advanced Security Code Scanning.

GitLab SAST (-f gitlab-sast — written to stdout): GitLab Secret Detection SAST report schema (v15.0.0), compatible with GitLab CI/CD Security tab.


False Positive Handling

Tier 3 automatically eliminates the vast majority of false positives. For persistent cases:

Method When to use
crenox:ignore comment One-off suppression for a specific line
Safe variable name (dummy_, fake_, mock_) Test or documentation values that look like secrets
Automatic Mock Value Filter Automatically ignores generic token rules if values contain mock/fake/test/dummy
Key File Entropy Bypass Skips raw line-by-line entropy checks on key extensions (.pem, .key, .rsa, .crt, .pub)
Function Call Protection Automatically filters out code function calls and methods containing parentheses
Source RHS Quote Enforcement Automatically skips unquoted RHS tokens (variables, struct types, function calls) in source code files
Rust/C++ Generic Type Filter Tokens containing < or > (e.g. Option<u64>, Vec<T>) are never reported as secrets
Lowercase Identifier Filter Tokens composed entirely of lowercase letters and underscores (e.g. pass_summaries) are rejected by generic rules
Git Commit SHA Filter 20-char and 40-char pure hex tokens are rejected by the high-entropy-hex rule to eliminate dependency pinning hashes
Container Digest Filter Docker and OCI container image digests (sha256:[a-f0-9]{64}) are automatically suppressed
SVG Vector Path Filter High-entropy SVG path coordinates (d="M...") are automatically recognized and ignored
Example Env Template Filter Sample configurations (.env.example, .env.sample) are excluded from scanning by default
Bun Lockfile Filter Bun lockfiles (bun.lock, bun.lockb) are automatically excluded from scans by default alongside npm, yarn, and pnpm lockfiles
Pytest & Test Framework Filter Python test naming conventions (test_*.py, *_test.py, conftest.py, test-*) are automatically recognized as test paths
Tier 1 Pattern Precedence Lines matched by specific Tier 1 pattern rules automatically suppress duplicate Tier 2 Shannon entropy alerts
Temporary Variable Filter Variables prefixed with temp_ or tmp_ are treated as safe runtime placeholders
YAML Key Name Filter Key names without values (e.g. api-key:) are detected and discarded before being reported
Seed Directory Suppression Files inside seed/ or seeds/ directories are treated as safe test data automatically
allowlist_patterns in config Known safe tokens used repeatedly across the codebase
Move to test file path Values in tests/, testdata/, *_test.go, .md are suppressed automatically
${ENV_VAR} reference syntax Replaced at runtime — not a hardcoded secret
exclude_paths in config Entire directories that should never be scanned
Raise entropy_threshold Codebase has many long high-entropy non-secret identifiers
# .crenox.yaml
allowlist_patterns:
  - "AKIAIOSFODNN7EXAMPLE"   # exact match
  - "sk_test_*"              # all Stripe test keys
  - "*-placeholder-*"

Running Tests

make test     # all tests with race detector
make bench    # benchmarks
make cover    # HTML coverage report → coverage.html
make lint     # staticcheck

Representative benchmark output (shows zero allocations on the hot scan path):

BenchmarkShannonSmall-8          1456561         793.4 ns/op          0 B/op        0 allocs/op
BenchmarkSearch-8                   2041       613,592 ns/op          0 B/op        0 allocs/op   <- 0 allocs
BenchmarkSearchWithHit-8            4171       292,044 ns/op          0 B/op        0 allocs/op
BenchmarkAutomatonBuild-8            692     1,909,695 ns/op    324,642 B/op      310 allocs/op
BenchmarkAnalyze-8                 10000       144,090 ns/op     14,880 B/op      107 allocs/op
BenchmarkFullPipeline-8              697     2,039,775 ns/op     67,483 B/op     1202 allocs/op

Contributing

Contributions are welcome. All contributors must agree to the Contributor License Agreement. By submitting a pull request you agree that your contribution is licensed to CrenoxHQ under the terms of the CLA. This protects the project's open-source continuity and dual-licensing sustainability model. Please see CONTRIBUTING.md for contribution guidelines.


Maintainer

Maintained by CrenoxHQ — https://github.com/crenoxhq


License

GNU Affero General Public License v3.0.

Commercial SaaS deployment or distribution of a modified version without releasing the source under AGPL-3.0 is prohibited. See LICENSE for full terms.


Zero-dependency, local secret detection.

About

Statically compiled, zero-dependency Git pre-commit secret scanner and credentials detector written in Go. An alternative to Gitleaks and git-secrets.

Topics

Resources

Contributing

Security policy

Stars

31 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages