{"id":49738732,"url":"https://github.com/betterleaks/betterleaks","last_synced_at":"2026-05-26T04:00:55.121Z","repository":{"id":339627068,"uuid":"1149107700","full_name":"betterleaks/betterleaks","owner":"betterleaks","description":"Scan the world (for secrets)","archived":false,"fork":false,"pushed_at":"2026-05-21T21:06:46.000Z","size":9986,"stargazers_count":965,"open_issues_count":45,"forks_count":64,"subscribers_count":7,"default_branch":"main","last_synced_at":"2026-05-22T04:14:20.814Z","etag":null,"topics":["cicd","credentials","developer-tools","devops","devsecops","git","github","gitleaks","go","golang","llm-tools","nhi","secret","secrets"],"latest_commit_sha":null,"homepage":"https://betterleaks.com","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/betterleaks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"docs/AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-03T18:21:41.000Z","updated_at":"2026-05-21T23:02:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/betterleaks/betterleaks","commit_stats":null,"previous_names":["betterleaks/betterleaks"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/betterleaks/betterleaks","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/betterleaks%2Fbetterleaks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/betterleaks%2Fbetterleaks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/betterleaks%2Fbetterleaks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/betterleaks%2Fbetterleaks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/betterleaks","download_url":"https://codeload.github.com/betterleaks/betterleaks/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/betterleaks%2Fbetterleaks/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33503230,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T03:12:49.672Z","status":"ssl_error","status_checked_at":"2026-05-26T03:12:47.976Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cicd","credentials","developer-tools","devops","devsecops","git","github","gitleaks","go","golang","llm-tools","nhi","secret","secrets"],"created_at":"2026-05-09T14:00:31.540Z","updated_at":"2026-05-26T04:00:55.109Z","avatar_url":"https://github.com/betterleaks.png","language":"Go","funding_links":[],"categories":["Go","Dependency intelligence"],"sub_categories":[],"readme":"# Betterleaks\n```\n     ○ \n     ○\nghp_ ● qOomCIZBWchHR4v5FPp9UiQRS9CyigrCkXXuIJQPfe63f12a\n     ○ \n```\n\nBetterleaks is a configurable, fast, and thorough secrets scanner. It is maintained by the folks who made Gitleaks, including the original author. \nCheck out this series of blog posts to learn how the detection engine works: 1. [Regex is all you need](https://lookingatcomputer.substack.com/p/regex-is-almost-all-you-need), 2. [Rare Not Random](https://lookingatcomputer.substack.com/p/rare-not-random), 3. [Express YourCELf](https://lookingatcomputer.substack.com/p/express-yourcelf-filtering-and-validating).\n\nDevelopment is supported by\n\u003ca href=\"https://www.aikido.dev\"\u003e\u003cimg src=\"docs/aikido_log.svg\" alt=\"Aikido Security\" width=\"80\" /\u003e\u003c/a\u003e\n\n### Notable Features\n\n| Feature | Description |\n| :--- | :--- |\n| **CEL-based filtering** | Write contextual rule filters that evaluate fragment (data chunks) attributes (like git author, commit message, and file path) and finding data to reduce false positives. If you're coming from Gitleaks, think of this feature as a more expressive `[[allowlist]]` system. |\n| **Secrets Validation** | Validate if a detected secret is active by making asynchronous HTTP requests directly from within the rule definition using CEL. |\n| **Token Efficiency filtering** | Filter out natural language false positives by using BPE tokenization to measure how \"rare\" or non-human a string is. |\n| **Fast scans** | Achieve fast performance through sane default parallelization settings, ahocorasick keyword filters, and re2. |\n| **New Sources** | Support for sources like GitHub, S3, and more. It's easy to add new sources too!   |\n| **Portability** | Runs on any modern OS/Arch. The small binary can be integrated in any system. |\n\n\n### Installation\n```\n# Package managers\nbrew install betterleaks\nbrew install betterleaks/tap/betterleaks\n\n# Fedora Linux\nsudo dnf install betterleaks\n\n# Containers\ndocker pull ghcr.io/betterleaks/betterleaks:latest\n\n# Source\ngit clone https://github.com/betterleaks/betterleaks\ncd betterleaks\nmake build\n```\n\n### Usage\n```\n# Scan Git\nbetterleaks git /path/to/repo -v --git-workers=16\n\n# Scan local filesystem\nbetterleaks dir /path/to/file/or/dir -v\n\n# Scan GitHub org\nbetterleaks github https://github.com/betterleaks\n# Scan GitHub user\nbetterleaks github https://github.com/cooluser123456789 --include issues,prs,actions,releases,gists\n# Scan specific resource, like a PR... but exclude the description (only scan comments)\nbetterleaks github https://github.com/betterleaks/betterleaks/pull/113\n\n# Scan a public s3 dataset (Common Crawl).\nbetterleaks s3 https://commoncrawl.s3.us-east-1.amazonaws.com/crawl-data/CC-MAIN-2018-17/segments/1524125937193.1/warc/\n# Enumerate and scan every bucket in a Cloudflare R2 account\nbetterleaks s3 'https://\u003caccount-id\u003e.r2.cloudflarestorage.com/*'\n\n# Scan stdin\ncat some_file.txt | betterleaks stdin -v\n```\n\nFor more advanced scanning examples check out the [scanning doc](docs/scanning.md).\n\n### Configuration\n\nBetterleaks' strength comes from its expressive configuration. Filtering and validation logic are defined as CEL. It is recommended you spend 30 minutes familiarizing yourself with [CEL](https://cel.dev) before writing filters and validators. `prefilter`s run before any regex matching occurs and only have access to the `attributes` map. `attributes` describe a resource like a git patch. Use `prefilter`s to quickly bail out before more expensive scanning happens. `filter`s, on the other hand, get evaluated post-regex match and have access to the `attributes` map and candidate `finding` data like `finding[\"secret\"]` or `finding[\"match\"]`.\n\n```toml\n# Global prefilter, it runs before expensive regex calls\nprefilter = '''\n(matchesAny(attributes[?\"path\"].orValue(\"\"), [\n  r\"\"\"(?i)\\.(?:bmp|gif|jpe?g|png|svg|tiff|pdf|exe)$\"\"\",\n  r\"\"\"(?:^|/)node_modules(?:/.*)?$\"\"\",\n  r\"\"\"(?:^|/)vendor(?:/.*)?$\"\"\"\n]))\n|| attributes[?\"git.author_name\"].orValue(\"\") == \"renovate[bot]\"\n'''\n\n# Global filter, it runs for _every_ candidate secret.\nfilter = '''\ncontainsAny(finding[\"secret\"], [\n  \"EXAMPLE\",\n  \"CHANGEME\",\n  \"YOUR_API_KEY_HERE\",\n  \"0000000000000000\"\n])\n'''\n\n# An array of tables that contain data on how to detect secrets\n[[rules]]\nid = \"github-fine-grained-pat\"\ndescription = \"GitHub Fine-Grained Personal Access Token, risking unauthorized repo access.\"\nregex = '''github_pat_\\w{82}'''\nkeywords = [\"github_pat_\"]\n\n# Rule-level filter\nfilter = '''\n(\n    attributes[?\"git.author_name\"].orValue(\"\") == \"ci-runner\" \u0026\u0026\n    attributes[?\"path\"].orValue(\"\").startsWith(\"mocks/\") \u0026\u0026\n    finding[\"secret\"].contains(\"TESTING\")\n)\n|| (entropy(finding[\"secret\"]) \u003c= 3.0)\n'''\n\n# Post-match-and-filter async validation check\nvalidate = '''\ncel.bind(r,\n  http.get(\"https://api.github.com/user\", {\n    \"Accept\": \"application/vnd.github+json\",\n    \"Authorization\": \"token \" + secret\n  }),\n  r.status == 200 \u0026\u0026 r.json.?login.orValue(\"\") != \"\" ? {\n    \"result\": \"valid\",\n    \"username\": r.json.?login.orValue(\"\"),\n    \"name\": r.json.?name.orValue(\"\"),\n    \"scopes\": r.headers[?\"x-oauth-scopes\"].orValue(\"\")\n  } : r.status in [401, 403] ? {\n    \"result\": \"invalid\",\n    \"reason\": \"Unauthorized\"\n  } : unknown(r)\n)\n'''\n```\n\nRefer to the default [betterleaks config](https://github.com/betterleaks/betterleaks/blob/main/config/betterleaks.toml) for examples and the [config docs](docs/config.md) for more information about the `betterleaks.toml` config.\n\n### Exit Codes\n\nSet the exit code when leaks are encountered with the --exit-code flag. Default exit codes below:\n\n```\n0 - no leaks present\n1 - leaks or error encountered\n126 - unknown flag\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetterleaks%2Fbetterleaks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbetterleaks%2Fbetterleaks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetterleaks%2Fbetterleaks/lists"}