├── testdata ├── expected │ ├── report │ │ ├── empty.json │ │ ├── junit_empty.xml │ │ ├── csv_simple.csv │ │ ├── json_simple.json │ │ └── junit_simple.xml │ └── git │ │ ├── small-branch-foo.txt │ │ └── small.txt ├── repos │ ├── small │ │ ├── dotGit │ │ │ ├── HEAD │ │ │ ├── COMMIT_EDITMSG │ │ │ ├── ORIG_HEAD │ │ │ ├── refs │ │ │ │ ├── heads │ │ │ │ │ ├── foo │ │ │ │ │ ├── main │ │ │ │ │ ├── api-pkg │ │ │ │ │ └── remove-secrets │ │ │ │ └── remotes │ │ │ │ │ └── origin │ │ │ │ │ ├── HEAD │ │ │ │ │ ├── foo │ │ │ │ │ ├── main │ │ │ │ │ └── api-pkg │ │ │ ├── description │ │ │ ├── index │ │ │ ├── FETCH_HEAD │ │ │ ├── packed-refs │ │ │ ├── logs │ │ │ │ ├── refs │ │ │ │ │ ├── remotes │ │ │ │ │ │ └── origin │ │ │ │ │ │ │ ├── foo │ │ │ │ │ │ │ ├── api-pkg │ │ │ │ │ │ │ ├── main │ │ │ │ │ │ │ └── HEAD │ │ │ │ │ └── heads │ │ │ │ │ │ ├── api-pkg │ │ │ │ │ │ ├── remove-secrets │ │ │ │ │ │ ├── foo │ │ │ │ │ │ └── main │ │ │ │ └── HEAD │ │ │ ├── objects │ │ │ │ ├── 15 │ │ │ │ │ └── 2888a42422b2ff5868b8d003d626120a9cb738 │ │ │ │ ├── 32 │ │ │ │ │ └── bfaec6697c1a693f71b183d3389ad2547bbb3c │ │ │ │ ├── 49 │ │ │ │ │ └── 1504d5a31946ce75e22554cc34203d8e5ff3ca │ │ │ │ ├── 53 │ │ │ │ │ └── cd7a3c6eb4937f413e3c25e4a9f39289afa69e │ │ │ │ ├── 78 │ │ │ │ │ └── 9ba677976d5db481de55c799d67acbf8e3f16a │ │ │ │ ├── 90 │ │ │ │ │ └── 6335481df9a4b48906c90318b4fac76b67fe73 │ │ │ │ ├── 98 │ │ │ │ │ └── 1ab8417104c91bb9a4657f5d436c760c0aff50 │ │ │ │ ├── 02 │ │ │ │ │ └── d85657604c34e7b7fbb324a0c6c8b13c2c3760 │ │ │ │ ├── 2e │ │ │ │ │ └── 1db472eeba53f06c4026ae4566ea022e36598e │ │ │ │ ├── 4f │ │ │ │ │ └── 77f1b3cc39d4b17e4cf4ba0a38f5daed9875b4 │ │ │ │ ├── 5c │ │ │ │ │ └── 547e4215d9594c3935bdfefdf4f500016a4112 │ │ │ │ ├── 8d │ │ │ │ │ └── c20a62e189d3446cfb6ec328dbd379d64feb20 │ │ │ │ ├── 9a │ │ │ │ │ └── 932e37eaa9fb64b09e47e5e859c9b2c8cb47ad │ │ │ │ ├── a1 │ │ │ │ │ └── 22b33c6bad3ee54724f52f2caad385ab1982ab │ │ │ │ ├── a5 │ │ │ │ │ └── caae6d742e49a33982f1fdc608ce861ea59be5 │ │ │ │ ├── a9 │ │ │ │ │ └── aa0c942dcef669a94f207a77426106b25efd1a │ │ │ │ ├── ac │ │ │ │ │ └── bef43fdb053ec01e8e16697536d47853460cbc │ │ │ │ ├── bc │ │ │ │ │ └── f47ef84f29bb7ed6e653d61fccd30d0ecce886 │ │ │ │ ├── ca │ │ │ │ │ └── da78a9bf157ec05573f19e682d211f811c2e2d │ │ │ │ ├── d8 │ │ │ │ │ └── 32479114dc6be7207edc7c37ce91dd11b93161 │ │ │ │ ├── da │ │ │ │ │ ├── 2622b4d97e32c5801511244b809144b6b3ea78 │ │ │ │ │ └── eb160a13200a3422e73296a6892784a113e0d6 │ │ │ │ ├── df │ │ │ │ │ └── 7eaa59fc89b3e7258167605db2e582f1a78e6f │ │ │ │ ├── e5 │ │ │ │ │ └── c0849a65c586eab87dcfc31fec74f0fd7c62cb │ │ │ │ ├── f1 │ │ │ │ │ └── b58b97808f8e744f6a23c693859df5b5968901 │ │ │ │ ├── fa │ │ │ │ │ └── 468655086f149d41a8e7db14ed054bebec7687 │ │ │ │ └── pack │ │ │ │ │ ├── pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.idx │ │ │ │ │ └── pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.pack │ │ │ ├── info │ │ │ │ └── exclude │ │ │ └── config │ │ ├── README.md │ │ ├── api │ │ │ ├── api.go │ │ │ ├── ignoreCommit.go │ │ │ └── ignoreGlobal.go │ │ ├── .gitleaksignore │ │ └── main.go │ ├── staged │ │ ├── dotGit │ │ │ ├── HEAD │ │ │ ├── COMMIT_EDITMSG │ │ │ ├── ORIG_HEAD │ │ │ ├── refs │ │ │ │ ├── heads │ │ │ │ │ ├── foo │ │ │ │ │ ├── api-pkg │ │ │ │ │ ├── main │ │ │ │ │ └── remove-secrets │ │ │ │ └── remotes │ │ │ │ │ └── origin │ │ │ │ │ ├── HEAD │ │ │ │ │ ├── foo │ │ │ │ │ ├── main │ │ │ │ │ └── api-pkg │ │ │ ├── description │ │ │ ├── FETCH_HEAD │ │ │ ├── index │ │ │ ├── packed-refs │ │ │ ├── logs │ │ │ │ ├── refs │ │ │ │ │ ├── remotes │ │ │ │ │ │ └── origin │ │ │ │ │ │ │ ├── api-pkg │ │ │ │ │ │ │ ├── foo │ │ │ │ │ │ │ ├── main │ │ │ │ │ │ │ └── HEAD │ │ │ │ │ └── heads │ │ │ │ │ │ ├── api-pkg │ │ │ │ │ │ ├── remove-secrets │ │ │ │ │ │ ├── foo │ │ │ │ │ │ └── main │ │ │ │ └── HEAD │ │ │ ├── objects │ │ │ │ ├── 15 │ │ │ │ │ └── 2888a42422b2ff5868b8d003d626120a9cb738 │ │ │ │ ├── 46 │ │ │ │ │ └── 18d7e4512b6b0b1dab85cf846d9f43474ec8be │ │ │ │ ├── 49 │ │ │ │ │ └── 1504d5a31946ce75e22554cc34203d8e5ff3ca │ │ │ │ ├── 65 │ │ │ │ │ └── 83d6db4a57bbeda62d50fc91649036d499418d │ │ │ │ ├── 66 │ │ │ │ │ └── bc70d0c0bfbb6468b3f90c3f1e9f2ddba02b43 │ │ │ │ ├── 78 │ │ │ │ │ └── 9ba677976d5db481de55c799d67acbf8e3f16a │ │ │ │ ├── 90 │ │ │ │ │ └── 6335481df9a4b48906c90318b4fac76b67fe73 │ │ │ │ ├── 02 │ │ │ │ │ └── d85657604c34e7b7fbb324a0c6c8b13c2c3760 │ │ │ │ ├── 2e │ │ │ │ │ └── 1db472eeba53f06c4026ae4566ea022e36598e │ │ │ │ ├── 5c │ │ │ │ │ └── 547e4215d9594c3935bdfefdf4f500016a4112 │ │ │ │ ├── 9a │ │ │ │ │ └── 932e37eaa9fb64b09e47e5e859c9b2c8cb47ad │ │ │ │ ├── a1 │ │ │ │ │ └── 22b33c6bad3ee54724f52f2caad385ab1982ab │ │ │ │ ├── a5 │ │ │ │ │ └── caae6d742e49a33982f1fdc608ce861ea59be5 │ │ │ │ ├── a9 │ │ │ │ │ └── aa0c942dcef669a94f207a77426106b25efd1a │ │ │ │ ├── b1 │ │ │ │ │ └── 6d768dd595a59f947abe087901183d219d7e54 │ │ │ │ ├── bc │ │ │ │ │ └── f47ef84f29bb7ed6e653d61fccd30d0ecce886 │ │ │ │ ├── bf │ │ │ │ │ └── 3f24164d7256b4021575cbdb2f97b98e6f057e │ │ │ │ ├── d8 │ │ │ │ │ └── 32479114dc6be7207edc7c37ce91dd11b93161 │ │ │ │ ├── da │ │ │ │ │ └── 2622b4d97e32c5801511244b809144b6b3ea78 │ │ │ │ ├── e5 │ │ │ │ │ └── c0849a65c586eab87dcfc31fec74f0fd7c62cb │ │ │ │ ├── f1 │ │ │ │ │ └── b58b97808f8e744f6a23c693859df5b5968901 │ │ │ │ └── pack │ │ │ │ │ ├── pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.idx │ │ │ │ │ └── pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.pack │ │ │ ├── info │ │ │ │ └── exclude │ │ │ └── config │ │ ├── .gitleaksignore │ │ ├── README.md │ │ ├── api │ │ │ └── api.go │ │ └── main.go │ ├── symlinks │ │ ├── file_symlink │ │ │ └── symlinked_id_ed25519 │ │ └── source_file │ │ │ └── id_ed25519 │ └── nogit │ │ ├── .gitleaksignore │ │ ├── api.go │ │ └── main.go ├── baseline │ ├── baseline.sarif │ ├── baseline.csv │ └── baseline.json └── config │ ├── path_only.toml │ ├── entropy_group.toml │ ├── bad_entropy_group.toml │ ├── allow_global_aws_re.toml │ ├── base.toml │ ├── generic.toml │ ├── escaped_character_group.toml │ ├── extend_2.toml │ ├── extend_1.toml │ ├── extend_3.toml │ ├── allow_path.toml │ ├── allow_aws_re.toml │ ├── allow_commit.toml │ └── generic_with_py_path.toml ├── .github ├── FUNDING.yml ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── maintenance.md │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── gitleaks.yml │ ├── test.yml │ └── release.yml ├── report ├── constants.go ├── json.go ├── finding_test.go ├── report.go ├── finding.go ├── csv.go ├── json_test.go ├── csv_test.go ├── report_test.go ├── sarif_test.go ├── junit.go └── junit_test.go ├── .gitignore ├── cmd ├── generate │ ├── secrets │ │ └── regen.go │ └── config │ │ └── rules │ │ ├── heroku.go │ │ ├── algolia.go │ │ ├── twilio.go │ │ ├── databricks.go │ │ ├── facebook.go │ │ ├── age.go │ │ ├── hubspot.go │ │ ├── clojars.go │ │ ├── frameio.go │ │ ├── npm.go │ │ ├── airtable.go │ │ ├── prefect.go │ │ ├── pulumi.go │ │ ├── readme.go │ │ ├── beamer.go │ │ ├── sentry.go │ │ ├── duffel.go │ │ ├── etsy.go │ │ ├── fastly.go │ │ ├── twitch.go │ │ ├── intercom.go │ │ ├── okta.go │ │ ├── flickr.go │ │ ├── mailchimp.go │ │ ├── rubygems.go │ │ ├── trello.go │ │ ├── zendesk.go │ │ ├── codecov.go │ │ ├── droneci.go │ │ ├── finnhub.go │ │ ├── adafruit.go │ │ ├── sendgrid.go │ │ ├── travisci.go │ │ ├── aws.go │ │ ├── datadog.go │ │ ├── contentful.go │ │ ├── freshbooks.go │ │ ├── mattermost.go │ │ ├── typeform.go │ │ ├── dynatrace.go │ │ ├── mapbox.go │ │ ├── netlify.go │ │ ├── postman.go │ │ ├── squarespace.go │ │ ├── gitter.go │ │ ├── pypi.go │ │ ├── kraken.go │ │ ├── stripe.go │ │ ├── openai.go │ │ ├── coinbase.go │ │ ├── gocardless.go │ │ ├── rapidapi.go │ │ ├── launchdarkly.go │ │ ├── sendinblue.go │ │ ├── hashicorp.go │ │ ├── doppler.go │ │ ├── shippo.go │ │ ├── nytimes.go │ │ ├── atlassian.go │ │ ├── privatekey.go │ │ ├── teams.go │ │ ├── definednetworking.go │ │ ├── adobe.go │ │ ├── easypost.go │ │ ├── square.go │ │ ├── vault.go │ │ ├── linear.go │ │ ├── alibaba.go │ │ ├── asana.go │ │ ├── bittrex.go │ │ ├── finicity.go │ │ ├── gcp.go │ │ ├── kucoin.go │ │ ├── snyk.go │ │ ├── authress.go │ │ ├── sendbird.go │ │ ├── sumologic.go │ │ ├── bitbucket.go │ │ ├── confluent.go │ │ ├── lob.go │ │ ├── linkedin.go │ │ ├── jwt.go │ │ ├── generic.go │ │ ├── dropbox.go │ │ ├── digitalocean.go │ │ ├── gitlab.go │ │ ├── messagebird.go │ │ ├── discord.go │ │ ├── flutterwave.go │ │ ├── mailgun.go │ │ ├── plaid.go │ │ ├── yandex.go │ │ ├── grafana.go │ │ ├── telegram.go │ │ ├── newrelic.go │ │ ├── shopify.go │ │ ├── config.tmpl │ │ ├── planetscale.go │ │ ├── sidekiq.go │ │ ├── github.go │ │ └── twitter.go └── version.go ├── config ├── utils.go ├── rule.go ├── allowlist.go └── allowlist_test.go ├── .pre-commit-hooks.yaml ├── Dockerfile ├── .goreleaser.yml ├── Makefile ├── main.go ├── scripts └── pre-commit.py ├── USERS.md ├── LICENSE ├── detect ├── location_test.go ├── baseline.go └── location.go └── go.mod /testdata/expected/report/empty.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/main 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/main 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/.gitleaksignore: -------------------------------------------------------------------------------- 1 | api/api.go:aws-access-key:6 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | add .gitleaksignore file 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | add .gitleaksignore test files 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/ORIG_HEAD: -------------------------------------------------------------------------------- 1 | cada78a9bf157ec05573f19e682d211f811c2e2d 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/ORIG_HEAD: -------------------------------------------------------------------------------- 1 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2 | -------------------------------------------------------------------------------- /testdata/repos/symlinks/file_symlink/symlinked_id_ed25519: -------------------------------------------------------------------------------- 1 | ../source_file/id_ed25519 -------------------------------------------------------------------------------- /testdata/repos/small/README.md: -------------------------------------------------------------------------------- 1 | # test 2 | This is a repo used for testing gitleaks 3 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/refs/heads/foo: -------------------------------------------------------------------------------- 1 | f1b58b97808f8e744f6a23c693859df5b5968901 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/refs/heads/main: -------------------------------------------------------------------------------- 1 | 53cd7a3c6eb4937f413e3c25e4a9f39289afa69e 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/refs/remotes/origin/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/remotes/origin/main 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/README.md: -------------------------------------------------------------------------------- 1 | # test 2 | This is a repo used for testing gitleaks 3 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/refs/heads/foo: -------------------------------------------------------------------------------- 1 | f1b58b97808f8e744f6a23c693859df5b5968901 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/refs/remotes/origin/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/remotes/origin/main 2 | -------------------------------------------------------------------------------- /testdata/repos/nogit/.gitleaksignore: -------------------------------------------------------------------------------- 1 | ../testdata/repos/nogit/api.go:aws-access-key:20 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/refs/heads/api-pkg: -------------------------------------------------------------------------------- 1 | a122b33c6bad3ee54724f52f2caad385ab1982ab 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/refs/heads/api-pkg: -------------------------------------------------------------------------------- 1 | a122b33c6bad3ee54724f52f2caad385ab1982ab 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/refs/heads/main: -------------------------------------------------------------------------------- 1 | bf3f24164d7256b4021575cbdb2f97b98e6f057e 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/refs/heads/remove-secrets: -------------------------------------------------------------------------------- 1 | a122b33c6bad3ee54724f52f2caad385ab1982ab 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/refs/remotes/origin/foo: -------------------------------------------------------------------------------- 1 | f1b58b97808f8e744f6a23c693859df5b5968901 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/refs/remotes/origin/main: -------------------------------------------------------------------------------- 1 | 2e1db472eeba53f06c4026ae4566ea022e36598e 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/refs/remotes/origin/foo: -------------------------------------------------------------------------------- 1 | f1b58b97808f8e744f6a23c693859df5b5968901 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/refs/remotes/origin/main: -------------------------------------------------------------------------------- 1 | 2e1db472eeba53f06c4026ae4566ea022e36598e 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [zricethezav] 4 | -------------------------------------------------------------------------------- /report/constants.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | const version = "v8.0.0" 4 | const driver = "gitleaks" 5 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/refs/remotes/origin/api-pkg: -------------------------------------------------------------------------------- 1 | a122b33c6bad3ee54724f52f2caad385ab1982ab 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/refs/heads/remove-secrets: -------------------------------------------------------------------------------- 1 | a122b33c6bad3ee54724f52f2caad385ab1982ab 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/refs/remotes/origin/api-pkg: -------------------------------------------------------------------------------- 1 | a122b33c6bad3ee54724f52f2caad385ab1982ab 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/index -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/FETCH_HEAD: -------------------------------------------------------------------------------- 1 | 2e1db472eeba53f06c4026ae4566ea022e36598e branch 'main' of github.com:gitleaks/test 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/FETCH_HEAD: -------------------------------------------------------------------------------- 1 | 2e1db472eeba53f06c4026ae4566ea022e36598e branch 'main' of github.com:gitleaks/test 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/index -------------------------------------------------------------------------------- /testdata/repos/small/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "fmt" 4 | 5 | func PrintHello() { 6 | fmt.Println("hello") 7 | } 8 | -------------------------------------------------------------------------------- /testdata/baseline/baseline.sarif: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/sarif-2.1.0.json", 3 | "version": "2.1.0", 4 | "runs": [ 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /testdata/config/path_only.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks config" 2 | 3 | [[rules]] 4 | description = "Python Files" 5 | id = "python-files-only" 6 | path = '''.py''' 7 | -------------------------------------------------------------------------------- /testdata/repos/small/.gitleaksignore: -------------------------------------------------------------------------------- 1 | api/ignoreGlobal.go:aws-access-key:20 2 | 53cd7a3c6eb4937f413e3c25e4a9f39289afa69e:api/ignoreCommit.go:aws-access-key:20 3 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/packed-refs: -------------------------------------------------------------------------------- 1 | # pack-refs with: peeled fully-peeled sorted 2 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 refs/remotes/origin/main 3 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/packed-refs: -------------------------------------------------------------------------------- 1 | # pack-refs with: peeled fully-peeled sorted 2 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 refs/remotes/origin/main 3 | -------------------------------------------------------------------------------- /testdata/baseline/baseline.csv: -------------------------------------------------------------------------------- 1 | RuleID,Commit,File,Secret,Match,StartLine,EndLine,StartColumn,EndColumn,Author,Message,Date,Email,Fingerprint 2 | 1,b,c,f,s,m,s,e,s,e,a,m,f,r,f -------------------------------------------------------------------------------- /testdata/expected/report/junit_empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/logs/refs/remotes/origin/foo: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896935 -0500 update by push 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/logs/refs/remotes/origin/api-pkg: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896552 -0500 update by push 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/2e/1db472eeba53f06c4026ae4566ea022e36598e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/2e/1db472eeba53f06c4026ae4566ea022e36598e -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/32/bfaec6697c1a693f71b183d3389ad2547bbb3c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/32/bfaec6697c1a693f71b183d3389ad2547bbb3c -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/49/1504d5a31946ce75e22554cc34203d8e5ff3ca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/49/1504d5a31946ce75e22554cc34203d8e5ff3ca -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/4f/77f1b3cc39d4b17e4cf4ba0a38f5daed9875b4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/4f/77f1b3cc39d4b17e4cf4ba0a38f5daed9875b4 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/53/cd7a3c6eb4937f413e3c25e4a9f39289afa69e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/53/cd7a3c6eb4937f413e3c25e4a9f39289afa69e -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/78/9ba677976d5db481de55c799d67acbf8e3f16a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/78/9ba677976d5db481de55c799d67acbf8e3f16a -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/8d/c20a62e189d3446cfb6ec328dbd379d64feb20: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/8d/c20a62e189d3446cfb6ec328dbd379d64feb20 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/98/1ab8417104c91bb9a4657f5d436c760c0aff50: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/98/1ab8417104c91bb9a4657f5d436c760c0aff50 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/ac/bef43fdb053ec01e8e16697536d47853460cbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/ac/bef43fdb053ec01e8e16697536d47853460cbc -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/ca/da78a9bf157ec05573f19e682d211f811c2e2d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/ca/da78a9bf157ec05573f19e682d211f811c2e2d -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/d8/32479114dc6be7207edc7c37ce91dd11b93161: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/d8/32479114dc6be7207edc7c37ce91dd11b93161 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/da/2622b4d97e32c5801511244b809144b6b3ea78: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/da/2622b4d97e32c5801511244b809144b6b3ea78 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/da/eb160a13200a3422e73296a6892784a113e0d6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/da/eb160a13200a3422e73296a6892784a113e0d6 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/df/7eaa59fc89b3e7258167605db2e582f1a78e6f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/df/7eaa59fc89b3e7258167605db2e582f1a78e6f -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/f1/b58b97808f8e744f6a23c693859df5b5968901: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/f1/b58b97808f8e744f6a23c693859df5b5968901 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/fa/468655086f149d41a8e7db14ed054bebec7687: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/fa/468655086f149d41a8e7db14ed054bebec7687 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/logs/refs/remotes/origin/api-pkg: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896552 -0500 update by push 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/logs/refs/remotes/origin/foo: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896935 -0500 update by push 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/2e/1db472eeba53f06c4026ae4566ea022e36598e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/2e/1db472eeba53f06c4026ae4566ea022e36598e -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/46/18d7e4512b6b0b1dab85cf846d9f43474ec8be: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/46/18d7e4512b6b0b1dab85cf846d9f43474ec8be -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/49/1504d5a31946ce75e22554cc34203d8e5ff3ca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/49/1504d5a31946ce75e22554cc34203d8e5ff3ca -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/65/83d6db4a57bbeda62d50fc91649036d499418d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/65/83d6db4a57bbeda62d50fc91649036d499418d -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/66/bc70d0c0bfbb6468b3f90c3f1e9f2ddba02b43: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/66/bc70d0c0bfbb6468b3f90c3f1e9f2ddba02b43 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/78/9ba677976d5db481de55c799d67acbf8e3f16a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/78/9ba677976d5db481de55c799d67acbf8e3f16a -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/b1/6d768dd595a59f947abe087901183d219d7e54: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/b1/6d768dd595a59f947abe087901183d219d7e54 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/bf/3f24164d7256b4021575cbdb2f97b98e6f057e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/bf/3f24164d7256b4021575cbdb2f97b98e6f057e -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/d8/32479114dc6be7207edc7c37ce91dd11b93161: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/d8/32479114dc6be7207edc7c37ce91dd11b93161 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/da/2622b4d97e32c5801511244b809144b6b3ea78: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/da/2622b4d97e32c5801511244b809144b6b3ea78 -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/f1/b58b97808f8e744f6a23c693859df5b5968901: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/f1/b58b97808f8e744f6a23c693859df5b5968901 -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/logs/refs/heads/api-pkg: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896543 -0500 branch: Created from HEAD 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/logs/refs/heads/api-pkg: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896543 -0500 branch: Created from HEAD 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/logs/refs/remotes/origin/main: -------------------------------------------------------------------------------- 1 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: fast-forward 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/logs/refs/remotes/origin/main: -------------------------------------------------------------------------------- 1 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: fast-forward 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/logs/refs/remotes/origin/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git 2 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.idx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.idx -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.pack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/small/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.pack -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/logs/refs/remotes/origin/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git 2 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.idx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.idx -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.pack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/gitleaks/master/testdata/repos/staged/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.pack -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description: 2 | Explain the purpose of the PR. 3 | 4 | ### Checklist: 5 | 6 | * [ ] Does your PR pass tests? 7 | * [ ] Have you written new tests for your changes? 8 | * [ ] Have you lint your code locally prior to submission? 9 | -------------------------------------------------------------------------------- /testdata/config/entropy_group.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks config" 2 | 3 | [[rules]] 4 | id = "discord-api-key" 5 | description = "Discord API key" 6 | regex = '''(?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{64})['\"]''' 7 | secretGroup = 3 8 | entropy = 3.5 9 | -------------------------------------------------------------------------------- /testdata/config/bad_entropy_group.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks config" 2 | 3 | [[rules]] 4 | id = "discord-api-key" 5 | description = "Discord API key" 6 | regex = '''(?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{64})['\"]''' 7 | secretGroup = 5 8 | entropy = 3.5 9 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /testdata/config/allow_global_aws_re.toml: -------------------------------------------------------------------------------- 1 | [[rules]] 2 | description = "AWS Access Key" 3 | id = "aws-access-key" 4 | regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' 5 | tags = ["key", "AWS"] 6 | 7 | [allowlist] 8 | regexes = ['''AKIALALEMEL33243OLIA'''] 9 | -------------------------------------------------------------------------------- /testdata/config/base.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks config" 2 | 3 | [extend] 4 | path="../testdata/config/extend_1.toml" 5 | 6 | [[rules]] 7 | description = "AWS Secret Key" 8 | id = "aws-secret-key" 9 | regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}''' 10 | tags = ["key", "AWS"] 11 | -------------------------------------------------------------------------------- /testdata/config/generic.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks config" 2 | 3 | [[rules]] 4 | description = "Generic API Key" 5 | id = "generic-api-key" 6 | regex = '''(?i)((key|api|token|secret|password)[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([0-9a-zA-Z\-_=]{8,64})['\"]''' 7 | entropy = 3.7 8 | secretGroup = 4 9 | -------------------------------------------------------------------------------- /testdata/expected/report/csv_simple.csv: -------------------------------------------------------------------------------- 1 | RuleID,Commit,File,SymlinkFile,Secret,Match,StartLine,EndLine,StartColumn,EndColumn,Author,Message,Date,Email,Fingerprint,Tags 2 | test-rule,0000000000000000,auth.py,,a secret,line containing secret,1,2,1,2,John Doe,opps,10-19-2003,johndoe@gmail.com,fingerprint,tag1 tag2 tag3 3 | -------------------------------------------------------------------------------- /testdata/config/escaped_character_group.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks config" 2 | # https://learnxinyminutes.com/docs/toml/ for toml reference 3 | 4 | [[rules]] 5 | id = "pypi-upload-token" 6 | description = "PyPI upload token" 7 | regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9\-_]{50,1000}''' 8 | tags = ["key", "pypi"] -------------------------------------------------------------------------------- /testdata/config/extend_2.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks extended 2" 2 | 3 | [extend] 4 | path="../testdata/config/extend_3.toml" 5 | 6 | [[rules]] 7 | description = "AWS Secret Key" 8 | id = "aws-secret-key-again" 9 | regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}''' 10 | tags = ["key", "AWS"] 11 | -------------------------------------------------------------------------------- /testdata/repos/staged/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "fmt" 4 | 5 | func PrintHello() { 6 | aws_token := "AKIALALEMEL33243OLIA" // fingerprint of that secret is added to .gitleaksignore 7 | aws_token2 := "AKIALALEMEL33243OLIA" // this one is not 8 | fmt.Println(aws_token) 9 | fmt.Println(aws_token2) 10 | } 11 | -------------------------------------------------------------------------------- /testdata/config/extend_1.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks extended 1" 2 | 3 | [extend] 4 | path="../testdata/config/extend_2.toml" 5 | 6 | [[rules]] 7 | description = "AWS Access Key" 8 | id = "aws-access-key" 9 | regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' 10 | tags = ["key", "AWS"] 11 | -------------------------------------------------------------------------------- /testdata/expected/git/small-branch-foo.txt: -------------------------------------------------------------------------------- 1 | import ( 2 | "fmt" 3 | "os" 4 | ) 5 | // seems safer 6 | aws_token := os.Getenv("AWS_TOKEN") 7 | package foo 8 | 9 | import "fmt" 10 | 11 | func Foo() { 12 | fmt.Println("foo") 13 | 14 | // seems safe 15 | aws_token := "AKIALALEMEL33243OLIA" 16 | fmt.Println(aws_token) 17 | } 18 | -------------------------------------------------------------------------------- /testdata/config/extend_3.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks extended 3" 2 | 3 | ## This should not be loaded since we can only extend configs to a depth of 3 4 | 5 | [[rules]] 6 | description = "AWS Secret Key" 7 | id = "aws-secret-key-again-again" 8 | regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}''' 9 | tags = ["key", "AWS"] 10 | -------------------------------------------------------------------------------- /testdata/config/allow_path.toml: -------------------------------------------------------------------------------- 1 | title = "simple config with allowlist for .go files" 2 | 3 | [[rules]] 4 | description = "AWS Access Key" 5 | id = "aws-access-key" 6 | regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' 7 | tags = ["key", "AWS"] 8 | [rules.allowlist] 9 | paths = ['''.go'''] 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.DS_STORE 8 | *.idea 9 | *.got 10 | gitleaks 11 | build 12 | 13 | # configs 14 | .gitleaks.toml 15 | cmd/generate/config/gitleaks.toml 16 | 17 | # test results 18 | testdata/expected/report/*.got.* 19 | 20 | # Test binary 21 | *.out 22 | 23 | dist/ 24 | -------------------------------------------------------------------------------- /report/json.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | func writeJson(findings []Finding, w io.WriteCloser) error { 9 | if len(findings) == 0 { 10 | findings = []Finding{} 11 | } 12 | defer w.Close() 13 | 14 | encoder := json.NewEncoder(w) 15 | encoder.SetIndent("", " ") 16 | return encoder.Encode(findings) 17 | } 18 | -------------------------------------------------------------------------------- /testdata/config/allow_aws_re.toml: -------------------------------------------------------------------------------- 1 | title = "simple config with allowlist for aws" 2 | 3 | [[rules]] 4 | description = "AWS Access Key" 5 | id = "aws-access-key" 6 | regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' 7 | tags = ["key", "AWS"] 8 | [rules.allowlist] 9 | regexes = ['''AKIALALEMEL33243OLIA'''] 10 | -------------------------------------------------------------------------------- /testdata/config/allow_commit.toml: -------------------------------------------------------------------------------- 1 | title = "simple config with allowlist for a specific commit" 2 | 3 | [[rules]] 4 | description = "AWS Access Key" 5 | id = "aws-access-key" 6 | regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' 7 | tags = ["key", "AWS"] 8 | [rules.allowlist] 9 | commits = ['''allowthiscommit'''] 10 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | ignorecase = true 7 | precomposeunicode = true 8 | [remote "origin"] 9 | url = git@github.com:gitleaks/test.git 10 | fetch = +refs/heads/*:refs/remotes/origin/* 11 | [branch "main"] 12 | remote = origin 13 | merge = refs/heads/main 14 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | ignorecase = true 7 | precomposeunicode = true 8 | [remote "origin"] 9 | url = git@github.com:gitleaks/test.git 10 | fetch = +refs/heads/*:refs/remotes/origin/* 11 | [branch "main"] 12 | remote = origin 13 | merge = refs/heads/main 14 | -------------------------------------------------------------------------------- /testdata/repos/nogit/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | var a = "initial" 8 | fmt.Println(a) 9 | 10 | var b, c int = 1, 2 11 | fmt.Println(b, c) 12 | 13 | var d = true 14 | fmt.Println(d) 15 | 16 | var e int 17 | fmt.Println(e) 18 | 19 | // opps I added a secret at line 20 20 | awsToken := "AKIALALEMEL33243OLIA" 21 | 22 | f := "apple" 23 | fmt.Println(f) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/generate/secrets/regen.go: -------------------------------------------------------------------------------- 1 | // Package reggen generates text based on regex definitions 2 | // This is a slightly altered version of https://github.com/lucasjones/reggen 3 | package secrets 4 | 5 | import ( 6 | "github.com/lucasjones/reggen" 7 | ) 8 | 9 | func NewSecret(regex string) string { 10 | g, err := reggen.NewGenerator(regex) 11 | if err != nil { 12 | panic(err) 13 | } 14 | return g.Generate(1) 15 | } 16 | -------------------------------------------------------------------------------- /testdata/repos/nogit/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | var a = "initial" 8 | fmt.Println(a) 9 | 10 | var b, c int = 1, 2 11 | fmt.Println(b, c) 12 | 13 | var d = true 14 | fmt.Println(d) 15 | 16 | var e int 17 | fmt.Println(e) 18 | 19 | // opps I added a secret at line 20 20 | awsToken := "AKIALALEMEL33243OLIA" 21 | 22 | f := "apple" 23 | fmt.Println(f) 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/maintenance.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Maintenance request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Additional context** 14 | Add any other context or screenshots about the feature request here. 15 | 16 | cc @zricethezav 17 | -------------------------------------------------------------------------------- /testdata/repos/small/api/ignoreCommit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | var a = "initial" 8 | fmt.Println(a) 9 | 10 | var b, c int = 1, 2 11 | fmt.Println(b, c) 12 | 13 | var d = true 14 | fmt.Println(d) 15 | 16 | var e int 17 | fmt.Println(e) 18 | 19 | // opps I added a secret at line 20 20 | awsToken := "AKIALALEMEL33243OLIA" 21 | 22 | f := "apple" 23 | fmt.Println(f) 24 | } 25 | -------------------------------------------------------------------------------- /testdata/repos/small/api/ignoreGlobal.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | var a = "initial" 8 | fmt.Println(a) 9 | 10 | var b, c int = 1, 2 11 | fmt.Println(b, c) 12 | 13 | var d = true 14 | fmt.Println(d) 15 | 16 | var e int 17 | fmt.Println(e) 18 | 19 | // opps I added a secret at line 20 20 | awsToken := "AKIALALEMEL33243OLIA" 21 | 22 | f := "apple" 23 | fmt.Println(f) 24 | } 25 | -------------------------------------------------------------------------------- /testdata/repos/small/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | 10 | var a = "initial" 11 | fmt.Println(a) 12 | 13 | var b, c int = 1, 2 14 | fmt.Println(b, c) 15 | 16 | var d = true 17 | fmt.Println(d) 18 | 19 | var e int 20 | fmt.Println(e) 21 | 22 | // load secret via env 23 | awsToken := os.Getenv("AWS_TOKEN") 24 | 25 | f := "apple" 26 | fmt.Println(f) 27 | } 28 | -------------------------------------------------------------------------------- /testdata/repos/staged/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | 10 | var a = "initial" 11 | fmt.Println(a) 12 | 13 | var b, c int = 1, 2 14 | fmt.Println(b, c) 15 | 16 | var d = true 17 | fmt.Println(d) 18 | 19 | var e int 20 | fmt.Println(e) 21 | 22 | // load secret via env 23 | awsToken := os.Getenv("AWS_TOKEN") 24 | 25 | f := "apple" 26 | fmt.Println(f) 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/gitleaks.yml: -------------------------------------------------------------------------------- 1 | name: gitleaks 2 | on: [push, workflow_dispatch] 3 | jobs: 4 | scan: 5 | name: gitleaks 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | with: 10 | fetch-depth: 0 11 | - uses: gitleaks/gitleaks-action@v2 12 | env: 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE}} 15 | -------------------------------------------------------------------------------- /testdata/repos/symlinks/source_file/id_ed25519: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 3 | QyNTUxOQAAACA8YWKYztuuvxUIMomc3zv0OdXCT57Cc2cRYu3TMbX9XAAAAJDiKO3C4ijt 4 | wgAAAAtzc2gtZWQyNTUxOQAAACA8YWKYztuuvxUIMomc3zv0OdXCT57Cc2cRYu3TMbX9XA 5 | AAAECzmj8DGxg5YHtBK4AmBttMXDQHsPAaCyYHQjJ4YujRBTxhYpjO266/FQgyiZzfO/Q5 6 | 1cJPnsJzZxFi7dMxtf1cAAAADHJvb3RAZGV2aG9zdAE= 7 | -----END OPENSSH PRIVATE KEY----- 8 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var Version = "version is set by build process" 10 | 11 | func init() { 12 | rootCmd.AddCommand(versionCmd) 13 | } 14 | 15 | var versionCmd = &cobra.Command{ 16 | Use: "version", 17 | Short: "display gitleaks version", 18 | Run: runVersion, 19 | } 20 | 21 | func runVersion(cmd *cobra.Command, args []string) { 22 | fmt.Println(Version) 23 | } 24 | -------------------------------------------------------------------------------- /config/utils.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | func anyRegexMatch(f string, res []*regexp.Regexp) bool { 8 | for _, re := range res { 9 | if regexMatched(f, re) { 10 | return true 11 | } 12 | } 13 | return false 14 | } 15 | 16 | func regexMatched(f string, re *regexp.Regexp) bool { 17 | if re == nil { 18 | return false 19 | } 20 | if re.FindString(f) != "" { 21 | return true 22 | } 23 | return false 24 | } 25 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: gitleaks 2 | name: Detect hardcoded secrets 3 | description: Detect hardcoded secrets using Gitleaks 4 | entry: gitleaks protect --verbose --redact --staged 5 | language: golang 6 | pass_filenames: false 7 | - id: gitleaks-docker 8 | name: Detect hardcoded secrets 9 | description: Detect hardcoded secrets using Gitleaks 10 | entry: zricethezav/gitleaks protect --verbose --redact --staged 11 | language: docker_image 12 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/logs/refs/heads/remove-secrets: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896362 -0500 branch: Created from HEAD 2 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 906335481df9a4b48906c90318b4fac76b67fe73 Zach Rice 1635896426 -0500 commit: load token via env var 3 | 906335481df9a4b48906c90318b4fac76b67fe73 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896518 -0500 commit: add api package 4 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/logs/refs/heads/remove-secrets: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896362 -0500 branch: Created from HEAD 2 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 906335481df9a4b48906c90318b4fac76b67fe73 Zach Rice 1635896426 -0500 commit: load token via env var 3 | 906335481df9a4b48906c90318b4fac76b67fe73 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896518 -0500 commit: add api package 4 | -------------------------------------------------------------------------------- /testdata/expected/report/json_simple.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Description": "", 4 | "StartLine": 1, 5 | "EndLine": 2, 6 | "StartColumn": 1, 7 | "EndColumn": 2, 8 | "Match": "line containing secret", 9 | "Secret": "a secret", 10 | "File": "auth.py", 11 | "SymlinkFile": "", 12 | "Commit": "0000000000000000", 13 | "Entropy": 0, 14 | "Author": "John Doe", 15 | "Email": "johndoe@gmail.com", 16 | "Date": "10-19-2003", 17 | "Message": "opps", 18 | "Tags": [], 19 | "RuleID": "test-rule", 20 | "Fingerprint": "" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/logs/refs/heads/foo: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896716 -0500 branch: Created from HEAD 2 | 2e1db472eeba53f06c4026ae4566ea022e36598e 491504d5a31946ce75e22554cc34203d8e5ff3ca Zach Rice 1635896886 -0500 commit: adding foo package with secret 3 | 491504d5a31946ce75e22554cc34203d8e5ff3ca f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896931 -0500 commit: removing secret from foo package 4 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/logs/refs/heads/foo: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896716 -0500 branch: Created from HEAD 2 | 2e1db472eeba53f06c4026ae4566ea022e36598e 491504d5a31946ce75e22554cc34203d8e5ff3ca Zach Rice 1635896886 -0500 commit: adding foo package with secret 3 | 491504d5a31946ce75e22554cc34203d8e5ff3ca f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896931 -0500 commit: removing secret from foo package 4 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/logs/refs/heads/main: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git 2 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: Fast-forward 3 | 2e1db472eeba53f06c4026ae4566ea022e36598e bf3f24164d7256b4021575cbdb2f97b98e6f057e Rafael Figueiredo 1679239434 -0300 commit: add .gitleaksignore file 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19 AS build 2 | WORKDIR /go/src/github.com/zricethezav/gitleaks 3 | COPY . . 4 | RUN VERSION=$(git describe --tags --abbrev=0) && \ 5 | CGO_ENABLED=0 go build -o bin/gitleaks -ldflags "-X="github.com/zricethezav/gitleaks/v8/cmd.Version=${VERSION} 6 | 7 | FROM alpine:3.16 8 | RUN adduser -D gitleaks && \ 9 | apk add --no-cache bash git openssh-client 10 | COPY --from=build /go/src/github.com/zricethezav/gitleaks/bin/* /usr/bin/ 11 | USER gitleaks 12 | 13 | RUN git config --global --add safe.directory '*' 14 | 15 | ENTRYPOINT ["gitleaks"] 16 | -------------------------------------------------------------------------------- /report/finding_test.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import "testing" 4 | 5 | func TestRedact(t *testing.T) { 6 | tests := []struct { 7 | findings []Finding 8 | redact bool 9 | }{ 10 | { 11 | redact: true, 12 | findings: []Finding{ 13 | { 14 | Secret: "line containing secret", 15 | Match: "secret", 16 | }, 17 | }}, 18 | } 19 | for _, test := range tests { 20 | for _, f := range test.findings { 21 | f.Redact() 22 | if f.Secret != "REDACTED" { 23 | t.Error("redact not redacting: ", f.Secret) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | pull_request: 8 | branches: 9 | - "*" 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: Set up Go 18 | uses: actions/setup-go@v2 19 | with: 20 | go-version: 1.19 21 | 22 | - name: Build 23 | run: go build -v ./... 24 | 25 | - name: Test 26 | run: make test 27 | 28 | - name: Validate Config 29 | run: cd cmd/generate/config && go run main.go 30 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/heroku.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/config" 5 | ) 6 | 7 | func Heroku() *config.Rule { 8 | // define rule 9 | r := config.Rule{ 10 | Description: "Heroku API Key", 11 | RuleID: "heroku-api-key", 12 | Regex: generateSemiGenericRegex([]string{"heroku"}, hex8_4_4_4_12()), 13 | SecretGroup: 1, 14 | Keywords: []string{"heroku"}, 15 | } 16 | 17 | // validate 18 | tps := []string{ 19 | `const HEROKU_KEY = "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 20 | } 21 | return validate(r, tps, nil) 22 | } 23 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | project_name: gitleaks 2 | 3 | builds: 4 | - main: main.go 5 | binary: gitleaks 6 | goos: 7 | - darwin 8 | - linux 9 | - windows 10 | goarch: 11 | - amd64 12 | - "386" 13 | - arm 14 | - arm64 15 | goarm: 16 | - "6" 17 | - "7" 18 | ldflags: 19 | - -s -w -X=github.com/zricethezav/gitleaks/v8/cmd.Version={{.Version}} 20 | archives: 21 | - builds: [gitleaks] 22 | format_overrides: 23 | - goos: windows 24 | format: zip 25 | replacements: 26 | amd64: x64 27 | 386: x32 28 | release: 29 | prerelease: true 30 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/algolia.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func AlgoliaApiKey() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Algolia API Key", 12 | RuleID: "algolia-api-key", 13 | Regex: generateSemiGenericRegex([]string{"algolia"}, `[a-z0-9]{32}`), 14 | Keywords: []string{"algolia"}, 15 | } 16 | 17 | // validate 18 | tps := []string{ 19 | "algolia_key := " + secrets.NewSecret(hex("32")), 20 | } 21 | return validate(r, tps, nil) 22 | } 23 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/twilio.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func Twilio() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Twilio API Key", 14 | RuleID: "twilio-api-key", 15 | Regex: regexp.MustCompile(`SK[0-9a-fA-F]{32}`), 16 | Keywords: []string{"twilio"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | "twilioAPIKey := \"SK" + secrets.NewSecret(hex("32")) + "\"", 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/databricks.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func Databricks() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Databricks API token", 12 | RuleID: "databricks-api-token", 13 | Regex: generateUniqueTokenRegex(`dapi[a-h0-9]{32}`), 14 | Keywords: []string{"dapi"}, 15 | } 16 | 17 | // validate 18 | tps := []string{ 19 | generateSampleSecret("databricks", "dapi"+secrets.NewSecret(hex("32"))), 20 | } 21 | return validate(r, tps, nil) 22 | } 23 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/facebook.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func Facebook() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Facebook Access Token", 12 | RuleID: "facebook", 13 | Regex: generateSemiGenericRegex([]string{"facebook"}, hex("32")), 14 | SecretGroup: 1, 15 | Keywords: []string{"facebook"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("facebook", secrets.NewSecret(hex("32"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/age.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/config" 7 | ) 8 | 9 | func AgeSecretKey() *config.Rule { 10 | // define rule 11 | r := config.Rule{ 12 | Description: "Age secret key", 13 | RuleID: "age secret key", 14 | Regex: regexp.MustCompile(`AGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L]{58}`), 15 | Keywords: []string{"AGE-SECRET-KEY-1"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | `apiKey := "AGE-SECRET-KEY-1QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ`, // gitleaks:allow 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/hubspot.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/config" 5 | ) 6 | 7 | func HubSpot() *config.Rule { 8 | // define rule 9 | r := config.Rule{ 10 | Description: "HubSpot API Token", 11 | RuleID: "hubspot-api-key", 12 | Regex: generateSemiGenericRegex([]string{"hubspot"}, 13 | `[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}`), 14 | SecretGroup: 1, 15 | Keywords: []string{"hubspot"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | `const hubspotKey = "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/clojars.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func Clojars() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Clojars API token", 14 | RuleID: "clojars-api-token", 15 | Regex: regexp.MustCompile(`(?i)(CLOJARS_)[a-z0-9]{60}`), 16 | Keywords: []string{"clojars"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("clojars", "CLOJARS_"+secrets.NewSecret(alphaNumeric("60"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test test-cover 2 | 3 | PKG=github.com/zricethezav/gitleaks 4 | VERSION := `git fetch --tags && git tag | sort -V | tail -1` 5 | LDFLAGS=-ldflags "-X=github.com/zricethezav/gitleaks/v8/cmd.Version=$(VERSION)" 6 | COVER=--cover --coverprofile=cover.out 7 | 8 | test-cover: 9 | go test -v ./... --race $(COVER) $(PKG) 10 | go tool cover -html=cover.out 11 | 12 | format: 13 | go fmt ./... 14 | 15 | test: format 16 | go vet ./... 17 | go test -v ./... --race $(PKG) 18 | 19 | build: format 20 | go vet ./... 21 | go mod tidy 22 | go build $(LDFLAGS) 23 | 24 | clean: 25 | find . -type f -name '*.got.*' -delete 26 | find . -type f -name '*.out' -delete 27 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/frameio.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func FrameIO() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Frame.io API token", 14 | RuleID: "frameio-api-token", 15 | Regex: regexp.MustCompile(`fio-u-(?i)[a-z0-9\-_=]{64}`), 16 | Keywords: []string{"fio-u-"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("frameio", "fio-u-"+secrets.NewSecret(alphaNumericExtended("64"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/npm.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func NPM() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "npm-access-token", 12 | Description: "npm access token", 13 | Regex: generateUniqueTokenRegex(`npm_[a-z0-9]{36}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "npm_", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("npmAccessToken", "npm_"+secrets.NewSecret(alphaNumeric("36"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/airtable.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func Airtable() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Airtable API Key", 12 | RuleID: "airtable-api-key", 13 | Regex: generateSemiGenericRegex([]string{"airtable"}, alphaNumeric("17")), 14 | SecretGroup: 1, 15 | Keywords: []string{"airtable"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("airtable", secrets.NewSecret(alphaNumeric("17"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/prefect.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func Prefect() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "prefect-api-token", 12 | Description: "Prefect API token", 13 | Regex: generateUniqueTokenRegex(`pnu_[a-z0-9]{36}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "pnu_", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("api-token", "pnu_"+secrets.NewSecret(alphaNumeric("36"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/pulumi.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func PulumiAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "pulumi-api-token", 12 | Description: "Pulumi API token", 13 | Regex: generateUniqueTokenRegex(`pul-[a-f0-9]{40}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "pul-", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("pulumi-api-token", "pul-"+secrets.NewSecret(hex("40"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/readme.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func ReadMe() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "readme-api-token", 12 | Description: "Readme API token", 13 | Regex: generateUniqueTokenRegex(`rdme_[a-z0-9]{70}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "rdme_", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("api-token", "rdme_"+secrets.NewSecret(alphaNumeric("70"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/beamer.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func Beamer() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Beamer API token", 12 | RuleID: "beamer-api-token", 13 | SecretGroup: 1, 14 | Regex: generateSemiGenericRegex([]string{"beamer"}, 15 | `b_[a-z0-9=_\-]{44}`), 16 | Keywords: []string{"beamer"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("beamer", "b_"+secrets.NewSecret(alphaNumericExtended("44"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/sentry.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func SentryAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "sentry-access-token", 12 | Description: "Sentry Access Token", 13 | Regex: generateSemiGenericRegex([]string{"sentry"}, hex("64")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "sentry", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("sentry", secrets.NewSecret(hex("64"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/duffel.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func Duffel() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | RuleID: "duffel-api-token", 14 | Description: "Duffel API token", 15 | Regex: regexp.MustCompile(`duffel_(test|live)_(?i)[a-z0-9_\-=]{43}`), 16 | Keywords: []string{"duffel"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("duffel", "duffel_test_"+secrets.NewSecret(alphaNumericExtended("43"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/etsy.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func EtsyAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "etsy-access-token", 12 | Description: "Etsy Access Token", 13 | Regex: generateSemiGenericRegex([]string{"etsy"}, alphaNumeric("24")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "etsy", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("etsy", secrets.NewSecret(alphaNumeric("24"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/fastly.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func FastlyAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Fastly API key", 12 | RuleID: "fastly-api-token", 13 | Regex: generateSemiGenericRegex([]string{"fastly"}, alphaNumericExtended("32")), 14 | SecretGroup: 1, 15 | Keywords: []string{"fastly"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("fastly", secrets.NewSecret(alphaNumericExtended("32"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/twitch.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func TwitchAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "twitch-api-token", 12 | Description: "Twitch API token", 13 | Regex: generateSemiGenericRegex([]string{"twitch"}, alphaNumeric("30")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "twitch", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("twitch", secrets.NewSecret(alphaNumeric("30"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/intercom.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func Intercom() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Intercom API Token", 12 | RuleID: "intercom-api-key", 13 | Regex: generateSemiGenericRegex([]string{"intercom"}, alphaNumericExtended("60")), 14 | SecretGroup: 1, 15 | Keywords: []string{"intercom"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("intercom", secrets.NewSecret(alphaNumericExtended("60"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/okta.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func OktaAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "okta-access-token", 12 | Description: "Okta Access Token", 13 | Regex: generateSemiGenericRegex([]string{"okta"}, 14 | alphaNumericExtended("42")), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "okta", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("okta", secrets.NewSecret(alphaNumeric("42"))), 24 | } 25 | return validate(r, tps, nil) 26 | } 27 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/flickr.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func FlickrAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "flickr-access-token", 12 | Description: "Flickr Access Token", 13 | Regex: generateSemiGenericRegex([]string{"flickr"}, alphaNumeric("32")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "flickr", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("flickr", secrets.NewSecret(alphaNumeric("32"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/mailchimp.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func MailChimp() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "mailchimp-api-key", 12 | Description: "Mailchimp API key", 13 | Regex: generateSemiGenericRegex([]string{"mailchimp"}, `[a-f0-9]{32}-us20`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "mailchimp", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("mailchimp", secrets.NewSecret(hex("32"))+"-us20"), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/rubygems.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func RubyGemsAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "rubygems-api-token", 12 | Description: "Rubygem API token", 13 | Regex: generateUniqueTokenRegex(`rubygems_[a-f0-9]{48}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "rubygems_", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("rubygemsAPIToken", "rubygems_"+secrets.NewSecret(hex("48"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/trello.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func TrelloAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "trello-access-token", 12 | Description: "Trello Access Token", 13 | Regex: generateSemiGenericRegex([]string{"trello"}, `[a-zA-Z-0-9]{32}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "trello", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("trello", secrets.NewSecret(`[a-zA-Z-0-9]{32}`)), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/zendesk.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func ZendeskSecretKey() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "zendesk-secret-key", 12 | Description: "Zendesk Secret Key", 13 | Regex: generateSemiGenericRegex([]string{"zendesk"}, alphaNumeric("40")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "zendesk", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("zendesk", secrets.NewSecret(alphaNumeric("40"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/codecov.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func CodecovAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "codecov-access-token", 12 | Description: "Codecov Access Token", 13 | Regex: generateSemiGenericRegex([]string{"codecov"}, alphaNumeric("32")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "codecov", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("codecov", secrets.NewSecret(alphaNumeric("32"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/droneci.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func DroneciAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "droneci-access-token", 12 | Description: "Droneci Access Token", 13 | Regex: generateSemiGenericRegex([]string{"droneci"}, alphaNumeric("32")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "droneci", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("droneci", secrets.NewSecret(alphaNumeric("32"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/finnhub.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func FinnhubAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "finnhub-access-token", 12 | Description: "Finnhub Access Token", 13 | Regex: generateSemiGenericRegex([]string{"finnhub"}, alphaNumeric("20")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "finnhub", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("finnhub", secrets.NewSecret(alphaNumeric("20"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/adafruit.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func AdafruitAPIKey() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Adafruit API Key", 12 | RuleID: "adafruit-api-key", 13 | Regex: generateSemiGenericRegex([]string{"adafruit"}, alphaNumericExtendedShort("32")), 14 | SecretGroup: 1, 15 | Keywords: []string{"adafruit"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("adafruit", secrets.NewSecret(alphaNumericExtendedShort("32"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/sendgrid.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func SendGridAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "sendgrid-api-token", 12 | Description: "SendGrid API token", 13 | Regex: generateUniqueTokenRegex(`SG\.(?i)[a-z0-9=_\-\.]{66}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "SG.", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("sengridAPIToken", "SG."+secrets.NewSecret(alphaNumericExtended("66"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/travisci.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func TravisCIAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "travisci-access-token", 12 | Description: "Travis CI Access Token", 13 | Regex: generateSemiGenericRegex([]string{"travis"}, alphaNumeric("22")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "travis", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("travis", secrets.NewSecret(alphaNumeric("22"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/aws.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/config" 7 | ) 8 | 9 | func AWS() *config.Rule { 10 | // define rule 11 | r := config.Rule{ 12 | Description: "AWS", 13 | RuleID: "aws-access-token", 14 | Regex: regexp.MustCompile( 15 | "(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}"), 16 | Keywords: []string{ 17 | "AKIA", 18 | "AGPA", 19 | "AIDA", 20 | "AROA", 21 | "AIPA", 22 | "ANPA", 23 | "ANVA", 24 | "ASIA", 25 | }, 26 | } 27 | 28 | // validate 29 | tps := []string{generateSampleSecret("AWS", "AKIALALEMEL33243OLIB")} // gitleaks:allow 30 | return validate(r, tps, nil) 31 | } 32 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/datadog.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func DatadogtokenAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "datadog-access-token", 12 | Description: "Datadog Access Token", 13 | Regex: generateSemiGenericRegex([]string{"datadog"}, 14 | alphaNumeric("40")), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "datadog", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("datadog", secrets.NewSecret(alphaNumeric("40"))), 24 | } 25 | return validate(r, tps, nil) 26 | } 27 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/contentful.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func Contentful() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Contentful delivery API token", 12 | RuleID: "contentful-delivery-api-token", 13 | Regex: generateSemiGenericRegex([]string{"contentful"}, 14 | alphaNumericExtended("43")), 15 | SecretGroup: 1, 16 | Keywords: []string{"contentful"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("contentful", secrets.NewSecret(alphaNumeric("43"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/freshbooks.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func FreshbooksAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "freshbooks-access-token", 12 | Description: "Freshbooks Access Token", 13 | Regex: generateSemiGenericRegex([]string{"freshbooks"}, alphaNumeric("64")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "freshbooks", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("freshbooks", secrets.NewSecret(alphaNumeric("64"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/mattermost.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func MattermostAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "mattermost-access-token", 12 | Description: "Mattermost Access Token", 13 | Regex: generateSemiGenericRegex([]string{"mattermost"}, alphaNumeric("26")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "mattermost", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("mattermost", secrets.NewSecret(alphaNumeric("26"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/typeform.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func Typeform() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "typeform-api-token", 12 | Description: "Typeform API token", 13 | Regex: generateSemiGenericRegex([]string{"typeform"}, 14 | `tfp_[a-z0-9\-_\.=]{59}`), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "tfp_", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("typeformAPIToken", "tfp_"+secrets.NewSecret(alphaNumericExtended("59"))), 24 | } 25 | return validate(r, tps, nil) 26 | } 27 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/dynatrace.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func Dynatrace() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Dynatrace API token", 14 | RuleID: "dynatrace-api-token", 15 | Regex: regexp.MustCompile(`dt0c01\.(?i)[a-z0-9]{24}\.[a-z0-9]{64}`), 16 | Keywords: []string{"dynatrace"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("dynatrace", "dt0c01."+secrets.NewSecret(alphaNumeric("24"))+"."+secrets.NewSecret(alphaNumeric("64"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/mapbox.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func MapBox() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "MapBox API token", 12 | RuleID: "mapbox-api-token", 13 | Regex: generateSemiGenericRegex([]string{"mapbox"}, `pk\.[a-z0-9]{60}\.[a-z0-9]{22}`), 14 | SecretGroup: 1, 15 | Keywords: []string{"mapbox"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("mapbox", "pk."+secrets.NewSecret(alphaNumeric("60"))+"."+secrets.NewSecret(alphaNumeric("22"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/netlify.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func NetlifyAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "netlify-access-token", 12 | Description: "Netlify Access Token", 13 | Regex: generateSemiGenericRegex([]string{"netlify"}, 14 | alphaNumericExtended("40,46")), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "netlify", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("netlify", secrets.NewSecret(alphaNumericExtended("40,46"))), 24 | } 25 | return validate(r, tps, nil) 26 | } 27 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/postman.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func PostManAPI() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "postman-api-token", 12 | Description: "Postman API token", 13 | Regex: generateUniqueTokenRegex(`PMAK-(?i)[a-f0-9]{24}\-[a-f0-9]{34}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "PMAK-", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("postmanAPItoken", "PMAK-"+secrets.NewSecret(hex("24"))+"-"+secrets.NewSecret(hex("34"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/squarespace.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func SquareSpaceAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "squarespace-access-token", 12 | Description: "Squarespace Access Token", 13 | Regex: generateSemiGenericRegex([]string{"squarespace"}, hex8_4_4_4_12()), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "squarespace", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("squarespace", secrets.NewSecret(hex8_4_4_4_12())), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/gitter.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func GitterAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "gitter-access-token", 12 | Description: "Gitter Access Token", 13 | Regex: generateSemiGenericRegex([]string{"gitter"}, 14 | alphaNumericExtendedShort("40")), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "gitter", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("gitter", 24 | secrets.NewSecret(alphaNumericExtendedShort("40"))), 25 | } 26 | return validate(r, tps, nil) 27 | } 28 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/pypi.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func PyPiUploadToken() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "PyPI upload token", 14 | RuleID: "pypi-upload-token", 15 | Regex: regexp.MustCompile( 16 | `pypi-AgEIcHlwaS5vcmc[A-Za-z0-9\-_]{50,1000}`), 17 | Keywords: []string{ 18 | "pypi-AgEIcHlwaS5vcmc", 19 | }, 20 | } 21 | 22 | // validate 23 | tps := []string{"pypiToken := \"pypi-AgEIcHlwaS5vcmc" + secrets.NewSecret(hex("32")) + 24 | secrets.NewSecret(hex("32")) + "\""} 25 | return validate(r, tps, nil) 26 | } 27 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | 7 | "github.com/rs/zerolog" 8 | "github.com/rs/zerolog/log" 9 | "github.com/zricethezav/gitleaks/v8/cmd" 10 | ) 11 | 12 | func main() { 13 | // send all logs to stdout 14 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) 15 | 16 | // this block sets up a go routine to listen for an interrupt signal 17 | // which will immediately exit gitleaks 18 | stopChan := make(chan os.Signal, 1) 19 | signal.Notify(stopChan, os.Interrupt) 20 | go listenForInterrupt(stopChan) 21 | 22 | cmd.Execute() 23 | } 24 | 25 | func listenForInterrupt(stopScan chan os.Signal) { 26 | <-stopScan 27 | log.Fatal().Msg("Interrupt signal received. Exiting...") 28 | } 29 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/kraken.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func KrakenAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "kraken-access-token", 12 | Description: "Kraken Access Token", 13 | Regex: generateSemiGenericRegex([]string{"kraken"}, 14 | alphaNumericExtendedLong("80,90")), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "kraken", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("kraken", 24 | secrets.NewSecret(alphaNumericExtendedLong("80,90"))), 25 | } 26 | return validate(r, tps, nil) 27 | } 28 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/stripe.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func StripeAccessToken() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Stripe Access Token", 14 | RuleID: "stripe-access-token", 15 | Regex: regexp.MustCompile(`(?i)(sk|pk)_(test|live)_[0-9a-z]{10,32}`), 16 | Keywords: []string{ 17 | "sk_test", 18 | "pk_test", 19 | "sk_live", 20 | "pk_live", 21 | }, 22 | } 23 | 24 | // validate 25 | tps := []string{"stripeToken := \"sk_test_" + secrets.NewSecret(alphaNumeric("30")) + "\""} 26 | return validate(r, tps, nil) 27 | } 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | 22 | cc @zricethezav 23 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/openai.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func OpenAI() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "openai-api-key", 12 | Description: "OpenAI API Key", 13 | Regex: generateUniqueTokenRegex(`sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "T3BlbkFJ", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("openaiApiKey", "sk-"+secrets.NewSecret(alphaNumeric("20"))+"T3BlbkFJ"+secrets.NewSecret(alphaNumeric("20"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/coinbase.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func CoinbaseAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "coinbase-access-token", 12 | Description: "Coinbase Access Token", 13 | Regex: generateSemiGenericRegex([]string{"coinbase"}, 14 | alphaNumericExtendedShort("64")), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "coinbase", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("coinbase", 24 | secrets.NewSecret(alphaNumericExtendedShort("64"))), 25 | } 26 | return validate(r, tps, nil) 27 | } 28 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/gocardless.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func GoCardless() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "gocardless-api-token", 12 | Description: "GoCardless API token", 13 | Regex: generateSemiGenericRegex([]string{"gocardless"}, `live_(?i)[a-z0-9\-_=]{40}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "live_", 17 | "gocardless", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("gocardless", "live_"+secrets.NewSecret(alphaNumericExtended("40"))), 24 | } 25 | return validate(r, tps, nil) 26 | } 27 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/rapidapi.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func RapidAPIAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "rapidapi-access-token", 12 | Description: "RapidAPI Access Token", 13 | Regex: generateSemiGenericRegex([]string{"rapidapi"}, 14 | alphaNumericExtendedShort("50")), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "rapidapi", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("rapidapi", 24 | secrets.NewSecret(alphaNumericExtendedShort("50"))), 25 | } 26 | return validate(r, tps, nil) 27 | } 28 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/launchdarkly.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func LaunchDarklyAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "launchdarkly-access-token", 12 | Description: "Launchdarkly Access Token", 13 | Regex: generateSemiGenericRegex([]string{"launchdarkly"}, alphaNumericExtended("40")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "launchdarkly", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("launchdarkly", secrets.NewSecret(alphaNumericExtended("40"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/sendinblue.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func SendInBlueAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "sendinblue-api-token", 12 | Description: "Sendinblue API token", 13 | Regex: generateUniqueTokenRegex(`xkeysib-[a-f0-9]{64}\-(?i)[a-z0-9]{16}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "xkeysib-", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("sendinblue", "xkeysib-"+secrets.NewSecret(hex("64"))+"-"+secrets.NewSecret(alphaNumeric("16"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/hashicorp.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func Hashicorp() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "HashiCorp Terraform user/org API token", 14 | RuleID: "hashicorp-tf-api-token", 15 | Regex: regexp.MustCompile(`(?i)[a-z0-9]{14}\.atlasv1\.[a-z0-9\-_=]{60,70}`), 16 | Keywords: []string{"atlasv1"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("hashicorpToken", secrets.NewSecret(hex("14"))+".atlasv1."+secrets.NewSecret(alphaNumericExtended("60,70"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/doppler.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func Doppler() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Doppler API token", 14 | RuleID: "doppler-api-token", 15 | Regex: regexp.MustCompile(`(dp\.pt\.)(?i)[a-z0-9]{43}`), 16 | Keywords: []string{"doppler"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("doppler", "dp.pt."+secrets.NewSecret(alphaNumeric("43"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | 26 | // TODO add additional doppler formats: 27 | // https://docs.doppler.com/reference/auth-token-formats 28 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/shippo.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func ShippoAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "shippo-api-token", 12 | Description: "Shippo API token", 13 | Regex: generateUniqueTokenRegex(`shippo_(live|test)_[a-f0-9]{40}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "shippo_", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("shippo", "shippo_live_"+secrets.NewSecret(hex("40"))), 23 | generateSampleSecret("shippo", "shippo_test_"+secrets.NewSecret(hex("40"))), 24 | } 25 | return validate(r, tps, nil) 26 | } 27 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/nytimes.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func NytimesAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "nytimes-access-token", 12 | Description: "Nytimes Access Token", 13 | Regex: generateSemiGenericRegex([]string{ 14 | "nytimes", "new-york-times,", "newyorktimes"}, 15 | alphaNumericExtended("32")), 16 | SecretGroup: 1, 17 | Keywords: []string{ 18 | "nytimes", 19 | "new-york-times", 20 | "newyorktimes", 21 | }, 22 | } 23 | 24 | // validate 25 | tps := []string{ 26 | generateSampleSecret("nytimes", secrets.NewSecret(alphaNumeric("32"))), 27 | } 28 | return validate(r, tps, nil) 29 | } 30 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/atlassian.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func Atlassian() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Atlassian API token", 12 | RuleID: "atlassian-api-token", 13 | Regex: generateSemiGenericRegex([]string{ 14 | "atlassian", "confluence", "jira"}, alphaNumeric("24")), 15 | SecretGroup: 1, 16 | Keywords: []string{"atlassian", "confluence", "jira"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("atlassian", secrets.NewSecret(alphaNumeric("24"))), 22 | generateSampleSecret("confluence", secrets.NewSecret(alphaNumeric("24"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | -------------------------------------------------------------------------------- /report/report.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | const ( 11 | // https://cwe.mitre.org/data/definitions/798.html 12 | CWE = "CWE-798" 13 | CWE_DESCRIPTION = "Use of Hard-coded Credentials" 14 | ) 15 | 16 | func Write(findings []Finding, cfg config.Config, ext string, reportPath string) error { 17 | file, err := os.Create(reportPath) 18 | if err != nil { 19 | return err 20 | } 21 | ext = strings.ToLower(ext) 22 | switch ext { 23 | case ".json", "json": 24 | err = writeJson(findings, file) 25 | case ".csv", "csv": 26 | err = writeCsv(findings, file) 27 | case ".xml", "junit": 28 | err = writeJunit(findings, file) 29 | case ".sarif", "sarif": 30 | err = writeSarif(cfg, findings, file) 31 | } 32 | 33 | return err 34 | } 35 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/privatekey.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/config" 7 | ) 8 | 9 | func PrivateKey() *config.Rule { 10 | // define rule 11 | r := config.Rule{ 12 | Description: "Private Key", 13 | RuleID: "private-key", 14 | Regex: regexp.MustCompile(`(?i)-----BEGIN[ A-Z0-9_-]{0,100}PRIVATE KEY( BLOCK)?-----[\s\S-]*KEY( BLOCK)?----`), 15 | Keywords: []string{"-----BEGIN"}, 16 | } 17 | 18 | // validate 19 | tps := []string{`-----BEGIN PRIVATE KEY----- 20 | anything 21 | -----END PRIVATE KEY-----`, 22 | `-----BEGIN RSA PRIVATE KEY----- 23 | abcdefghijklmnopqrstuvwxyz 24 | -----END RSA PRIVATE KEY----- 25 | `, 26 | `-----BEGIN PRIVATE KEY BLOCK----- 27 | anything 28 | -----END PRIVATE KEY BLOCK-----`, 29 | } // gitleaks:allow 30 | return validate(r, tps, nil) 31 | } 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **Basic Info (please complete the following information):** 23 | - OS: 24 | - Gitleaks Version: 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | 29 | cc @zricethezav 30 | 31 | 38 | -------------------------------------------------------------------------------- /scripts/pre-commit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Helper script to be used as a pre-commit hook.""" 3 | import os 4 | import sys 5 | import subprocess 6 | 7 | 8 | def gitleaksEnabled(): 9 | """Determine if the pre-commit hook for gitleaks is enabled.""" 10 | out = subprocess.getoutput("git config --bool hooks.gitleaks") 11 | if out == "false": 12 | return False 13 | return True 14 | 15 | 16 | if gitleaksEnabled(): 17 | exitCode = os.WEXITSTATUS(os.system('gitleaks protect -v --staged')) 18 | if exitCode == 1: 19 | print('''Warning: gitleaks has detected sensitive information in your changes. 20 | To disable the gitleaks precommit hook run the following command: 21 | 22 | git config hooks.gitleaks false 23 | ''') 24 | sys.exit(1) 25 | else: 26 | print('gitleaks precommit disabled\ 27 | (enable with `git config hooks.gitleaks true`)') 28 | -------------------------------------------------------------------------------- /testdata/config/generic_with_py_path.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks config" 2 | 3 | [[rules]] 4 | description = "Generic API Key" 5 | id = "generic-api-key" 6 | regex = '''(?i)((key|api|token|secret|password)[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([0-9a-zA-Z\-_=]{8,64})['\"]''' 7 | path = '''.py''' 8 | entropy = 3.7 9 | secretGroup = 4 10 | 11 | [allowlist] 12 | description = "global allow lists" 13 | regexes = [ 14 | '''219-09-9999''', 15 | '''078-05-1120''', 16 | '''(9[0-9]{2}|666)-\d{2}-\d{4}''', 17 | '''process''', 18 | '''getenv''', 19 | '''\.env''', 20 | '''env\(''', 21 | '''env\.''', 22 | '''setting''', 23 | '''load''', 24 | '''token''', 25 | '''password''', 26 | '''secret''', 27 | '''api\_key''', 28 | '''apikey''', 29 | '''api\-key''', 30 | ] 31 | paths = [ 32 | '''gitleaks.toml''', 33 | '''(.*?)(jpg|gif|doc|pdf|bin|svg|socket)$''', 34 | '''(go.mod|go.sum)$''' 35 | ] 36 | 37 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/logs/refs/heads/main: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git 2 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: Fast-forward 3 | 2e1db472eeba53f06c4026ae4566ea022e36598e 4f77f1b3cc39d4b17e4cf4ba0a38f5daed9875b4 Richard Gomez 1681920523 -0400 commit: add .gitleaksignore test 4 | 4f77f1b3cc39d4b17e4cf4ba0a38f5daed9875b4 cada78a9bf157ec05573f19e682d211f811c2e2d Richard Gomez 1681920658 -0400 commit (amend): add .gitleaksignore test 5 | cada78a9bf157ec05573f19e682d211f811c2e2d 2e1db472eeba53f06c4026ae4566ea022e36598e Richard Gomez 1681920730 -0400 reset: moving to HEAD~ 6 | 2e1db472eeba53f06c4026ae4566ea022e36598e 53cd7a3c6eb4937f413e3c25e4a9f39289afa69e Richard Gomez 1681920759 -0400 commit: add .gitleaksignore test files 7 | -------------------------------------------------------------------------------- /USERS.md: -------------------------------------------------------------------------------- 1 | ## Who uses Gitleaks? 2 | 3 | As the Gitleaks Community grows, we'd like to keep a list of our users. 4 | 5 | Here's a running list of some organizations using Gitleaks[^1]: 6 | 7 | 1. [GitLab](https://docs.gitlab.com/ee/user/application_security/secret_detection/) 8 | 1. [Gitleaks](https://gitleaks.io) 9 | 1. [GoReleaser](https://goreleaser.com) 10 | 2. [Trendyol](https://trendyol.com) 11 | 12 | Feel free to [add yours](https://github.com/zricethezav/gitleaks/edit/master/USERS.md)! 13 | 14 | 20 | 21 | [^1]: Entries were either added by the companies themselves or by the maintainers after seeing it in the wild. 22 | You can see all public repositories using Gitleaks or Gitleaks-Action by [searching on GitHub](https://github.com/search?q=gitleaks). 23 | 24 | 27 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/teams.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func TeamsWebhook() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Microsoft Teams Webhook", 14 | RuleID: "microsoft-teams-webhook", 15 | Regex: regexp.MustCompile( 16 | `https:\/\/[a-z0-9]+\.webhook\.office\.com\/webhookb2\/[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}@[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}\/IncomingWebhook\/[a-z0-9]{32}\/[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}`), 17 | Keywords: []string{ 18 | "webhook.office.com", 19 | "webhookb2", 20 | "IncomingWebhook", 21 | }, 22 | } 23 | 24 | // validate 25 | tps := []string{ 26 | "https://mycompany.webhook.office.com/webhookb2/" + secrets.NewSecret(`[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}@[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}\/IncomingWebhook\/[a-z0-9]{32}\/[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}`), // gitleaks:allow 27 | } 28 | return validate(r, tps, nil) 29 | } 30 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/definednetworking.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func DefinedNetworkingAPIToken() *config.Rule { 9 | // Define Rule 10 | r := config.Rule{ 11 | // Human redable description of the rule 12 | Description: "Defined Networking API token", 13 | 14 | // Unique ID for the rule 15 | RuleID: "defined-networking-api-token", 16 | 17 | // Regex capture group for the actual secret 18 | SecretGroup: 1, 19 | 20 | // Regex used for detecting secrets. See regex section below for more details 21 | Regex: generateSemiGenericRegex([]string{"dnkey"}, `dnkey-[a-z0-9=_\-]{26}-[a-z0-9=_\-]{52}`), 22 | 23 | // Keywords used for string matching on fragments (think of this as a prefilter) 24 | Keywords: []string{"dnkey"}, 25 | } 26 | 27 | // validate 28 | tps := []string{ 29 | generateSampleSecret("dnkey", "dnkey-"+secrets.NewSecret(alphaNumericExtended("26"))+"-"+secrets.NewSecret(alphaNumericExtended("52"))), 30 | } 31 | return validate(r, tps, nil) 32 | } 33 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/adobe.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func AdobeClientID() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Adobe Client ID (OAuth Web)", 12 | RuleID: "adobe-client-id", 13 | Regex: generateSemiGenericRegex([]string{"adobe"}, hex("32")), 14 | SecretGroup: 1, 15 | Keywords: []string{"adobe"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("adobe", secrets.NewSecret(hex("32"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | 25 | func AdobeClientSecret() *config.Rule { 26 | // define rule 27 | r := config.Rule{ 28 | Description: "Adobe Client Secret", 29 | RuleID: "adobe-client-secret", 30 | Regex: generateUniqueTokenRegex(`(p8e-)(?i)[a-z0-9]{32}`), 31 | Keywords: []string{"p8e-"}, 32 | } 33 | 34 | // validate 35 | tps := []string{ 36 | "adobeClient := \"p8e-" + secrets.NewSecret(hex("32")) + "\"", 37 | } 38 | return validate(r, tps, nil) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/easypost.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func EasyPost() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "EasyPost API token", 14 | RuleID: "easypost-api-token", 15 | Regex: regexp.MustCompile(`\bEZAK(?i)[a-z0-9]{54}`), 16 | Keywords: []string{"EZAK"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("EZAK", "EZAK"+secrets.NewSecret(alphaNumeric("54"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | 26 | func EasyPostTestAPI() *config.Rule { 27 | // define rule 28 | r := config.Rule{ 29 | Description: "EasyPost test API token", 30 | RuleID: "easypost-test-api-token", 31 | Regex: regexp.MustCompile(`\bEZTK(?i)[a-z0-9]{54}`), 32 | Keywords: []string{"EZTK"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("EZTK", "EZTK"+secrets.NewSecret(alphaNumeric("54"))), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/square.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func SquareAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "square-access-token", 12 | Description: "Square Access Token", 13 | Regex: generateUniqueTokenRegex(`sq0atp-[0-9A-Za-z\-_]{22}`), 14 | Keywords: []string{"sq0atp-"}, 15 | } 16 | 17 | // validate 18 | tps := []string{ 19 | generateSampleSecret("square", secrets.NewSecret(`sq0atp-[0-9A-Za-z\-_]{22}`)), 20 | } 21 | return validate(r, tps, nil) 22 | } 23 | 24 | func SquareSecret() *config.Rule { 25 | // define rule 26 | r := config.Rule{ 27 | RuleID: "square-secret", 28 | Description: "Square Secret", 29 | Regex: generateUniqueTokenRegex(`sq0csp-[0-9A-Za-z\\-_]{43}`), 30 | Keywords: []string{"sq0csp-"}, 31 | } 32 | 33 | // validate 34 | tps := []string{ 35 | generateSampleSecret("square", secrets.NewSecret(`sq0csp-[0-9A-Za-z\\-_]{43}`)), 36 | } 37 | return validate(r, tps, nil) 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Zachary Rice 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/vault.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func VaultServiceToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Vault Service Token", 12 | RuleID: "vault-service-token", 13 | Regex: generateUniqueTokenRegex(`hvs\.[a-z0-9_-]{90,100}`), 14 | Keywords: []string{"hvs"}, 15 | } 16 | 17 | // validate 18 | tps := []string{ 19 | generateSampleSecret("vault", "hvs."+secrets.NewSecret(alphaNumericExtendedShort("90"))), 20 | } 21 | return validate(r, tps, nil) 22 | } 23 | 24 | func VaultBatchToken() *config.Rule { 25 | // define rule 26 | r := config.Rule{ 27 | Description: "Vault Batch Token", 28 | RuleID: "vault-batch-token", 29 | Regex: generateUniqueTokenRegex(`hvb\.[a-z0-9_-]{138,212}`), 30 | Keywords: []string{"hvb"}, 31 | } 32 | 33 | // validate 34 | tps := []string{ 35 | generateSampleSecret("vault", "hvb."+secrets.NewSecret(alphaNumericExtendedShort("138"))), 36 | } 37 | return validate(r, tps, nil) 38 | } 39 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/linear.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func LinearAPIToken() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Linear API Token", 14 | RuleID: "linear-api-key", 15 | Regex: regexp.MustCompile(`lin_api_(?i)[a-z0-9]{40}`), 16 | Keywords: []string{"lin_api_"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("linear", "lin_api_"+secrets.NewSecret(alphaNumeric("40"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | 26 | func LinearClientSecret() *config.Rule { 27 | // define rule 28 | r := config.Rule{ 29 | Description: "Linear Client Secret", 30 | RuleID: "linear-client-secret", 31 | Regex: generateSemiGenericRegex([]string{"linear"}, hex("32")), 32 | Keywords: []string{"linear"}, 33 | SecretGroup: 1, 34 | } 35 | 36 | // validate 37 | tps := []string{ 38 | generateSampleSecret("linear", secrets.NewSecret(hex("32"))), 39 | } 40 | return validate(r, tps, nil) 41 | } 42 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/alibaba.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func AlibabaAccessKey() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Alibaba AccessKey ID", 12 | RuleID: "alibaba-access-key-id", 13 | Regex: generateUniqueTokenRegex(`(LTAI)(?i)[a-z0-9]{20}`), 14 | Keywords: []string{"LTAI"}, 15 | } 16 | 17 | // validate 18 | tps := []string{ 19 | "alibabaKey := \"LTAI" + secrets.NewSecret(hex("20")) + "\"", 20 | } 21 | return validate(r, tps, nil) 22 | } 23 | 24 | // TODO 25 | func AlibabaSecretKey() *config.Rule { 26 | // define rule 27 | r := config.Rule{ 28 | Description: "Alibaba Secret Key", 29 | RuleID: "alibaba-secret-key", 30 | Regex: generateSemiGenericRegex([]string{"alibaba"}, 31 | alphaNumeric("30")), 32 | SecretGroup: 1, 33 | Keywords: []string{"alibaba"}, 34 | } 35 | 36 | // validate 37 | tps := []string{ 38 | generateSampleSecret("alibaba", secrets.NewSecret(alphaNumeric("30"))), 39 | } 40 | return validate(r, tps, nil) 41 | } 42 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/asana.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func AsanaClientID() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Asana Client ID", 12 | RuleID: "asana-client-id", 13 | Regex: generateSemiGenericRegex([]string{"asana"}, numeric("16")), 14 | SecretGroup: 1, 15 | Keywords: []string{"asana"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("asana", secrets.NewSecret(numeric("16"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | 25 | func AsanaClientSecret() *config.Rule { 26 | // define rule 27 | r := config.Rule{ 28 | Description: "Asana Client Secret", 29 | RuleID: "asana-client-secret", 30 | Regex: generateSemiGenericRegex([]string{"asana"}, alphaNumeric("32")), 31 | SecretGroup: 1, 32 | Keywords: []string{"asana"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("asana", secrets.NewSecret(alphaNumeric("32"))), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/bittrex.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func BittrexAccessKey() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Bittrex Access Key", 12 | RuleID: "bittrex-access-key", 13 | Regex: generateSemiGenericRegex([]string{"bittrex"}, alphaNumeric("32")), 14 | SecretGroup: 1, 15 | Keywords: []string{"bittrex"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("bittrex", secrets.NewSecret(alphaNumeric("32"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | 25 | func BittrexSecretKey() *config.Rule { 26 | // define rule 27 | r := config.Rule{ 28 | Description: "Bittrex Secret Key", 29 | RuleID: "bittrex-secret-key", 30 | Regex: generateSemiGenericRegex([]string{"bittrex"}, alphaNumeric("32")), 31 | SecretGroup: 1, 32 | Keywords: []string{"bittrex"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("bittrex", secrets.NewSecret(alphaNumeric("32"))), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/finicity.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func FinicityClientSecret() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Finicity Client Secret", 12 | RuleID: "finicity-client-secret", 13 | Regex: generateSemiGenericRegex([]string{"finicity"}, alphaNumeric("20")), 14 | SecretGroup: 1, 15 | Keywords: []string{"finicity"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("finicity", secrets.NewSecret(alphaNumeric("20"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | 25 | func FinicityAPIToken() *config.Rule { 26 | // define rule 27 | r := config.Rule{ 28 | Description: "Finicity API token", 29 | RuleID: "finicity-api-token", 30 | Regex: generateSemiGenericRegex([]string{"finicity"}, hex("32")), 31 | SecretGroup: 1, 32 | Keywords: []string{"finicity"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("finicity", secrets.NewSecret(hex("32"))), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/gcp.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | // TODO this one could probably use some work 11 | func GCPServiceAccount() *config.Rule { 12 | // define rule 13 | r := config.Rule{ 14 | Description: "Google (GCP) Service-account", 15 | RuleID: "gcp-service-account", 16 | Regex: regexp.MustCompile(`\"type\": \"service_account\"`), 17 | Keywords: []string{`\"type\": \"service_account\"`}, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | `"type": "service_account"`, 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | 27 | func GCPAPIKey() *config.Rule { 28 | // define rule 29 | r := config.Rule{ 30 | RuleID: "gcp-api-key", 31 | Description: "GCP API key", 32 | Regex: generateUniqueTokenRegex(`AIza[0-9A-Za-z\\-_]{35}`), 33 | SecretGroup: 1, 34 | Keywords: []string{ 35 | "AIza", 36 | }, 37 | } 38 | 39 | // validate 40 | tps := []string{ 41 | generateSampleSecret("gcp", secrets.NewSecret(`AIza[0-9A-Za-z\\-_]{35}`)), 42 | } 43 | return validate(r, tps, nil) 44 | } 45 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/kucoin.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func KucoinAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "kucoin-access-token", 12 | Description: "Kucoin Access Token", 13 | Regex: generateSemiGenericRegex([]string{"kucoin"}, hex("24")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "kucoin", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("kucoin", secrets.NewSecret(hex("24"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | 27 | func KucoinSecretKey() *config.Rule { 28 | // define rule 29 | r := config.Rule{ 30 | RuleID: "kucoin-secret-key", 31 | Description: "Kucoin Secret Key", 32 | Regex: generateSemiGenericRegex([]string{"kucoin"}, hex8_4_4_4_12()), 33 | SecretGroup: 1, 34 | Keywords: []string{ 35 | "kucoin", 36 | }, 37 | } 38 | 39 | // validate 40 | tps := []string{ 41 | generateSampleSecret("kucoin", secrets.NewSecret(hex8_4_4_4_12())), 42 | } 43 | return validate(r, tps, nil) 44 | } 45 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/snyk.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/config" 5 | ) 6 | 7 | func Snyk() *config.Rule { 8 | // define rule 9 | r := config.Rule{ 10 | Description: "Snyk API token", 11 | RuleID: "snyk-api-token", 12 | SecretGroup: 1, 13 | Regex: generateSemiGenericRegex([]string{"snyk"}, hex8_4_4_4_12()), 14 | Keywords: []string{"snyk"}, 15 | } 16 | 17 | // validate 18 | tps := []string{ 19 | `const SNYK_TOKEN = "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 20 | `const SNYK_KEY = "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 21 | `const SNYK = "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 22 | `SNYK = "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 23 | `SNYK_TOKEN := "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 24 | `SNYK_TOKEN ::= "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 25 | `SNYK_TOKEN :::= "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 26 | `SNYK_TOKEN ?= "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 27 | } 28 | return validate(r, tps, nil) 29 | } 30 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/authress.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func Authress() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Authress Service Client Access Key", 14 | RuleID: "authress-service-client-access-key", 15 | SecretGroup: 1, 16 | Regex: generateUniqueTokenRegex(`(?:sc|ext|scauth|authress)_[a-z0-9]{5,30}\.[a-z0-9]{4,6}\.acc_[a-z0-9-]{10,32}\.[a-z0-9+/_=-]{30,120}`), 17 | Keywords: []string{"sc_", "ext_", "scauth_", "authress_"}, 18 | } 19 | 20 | // validate 21 | // https://authress.io/knowledge-base/docs/authorization/service-clients/secrets-scanning/#1-detection 22 | service_client_id := "sc_" + alphaNumeric("10") 23 | access_key_id := alphaNumeric("4") 24 | account_id := "acc_" + alphaNumeric("10") 25 | signature_key := alphaNumericExtendedShort("40") 26 | 27 | tps := []string{ 28 | generateSampleSecret("authress", secrets.NewSecret(fmt.Sprintf(`%s\.%s\.%s\.%s`, service_client_id, access_key_id, account_id, signature_key))), 29 | } 30 | return validate(r, tps, nil) 31 | } 32 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/sendbird.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func SendbirdAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "sendbird-access-token", 12 | Description: "Sendbird Access Token", 13 | Regex: generateSemiGenericRegex([]string{"sendbird"}, hex("40")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "sendbird", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("sendbird", secrets.NewSecret(hex("40"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | 27 | func SendbirdAccessID() *config.Rule { 28 | // define rule 29 | r := config.Rule{ 30 | RuleID: "sendbird-access-id", 31 | Description: "Sendbird Access ID", 32 | Regex: generateSemiGenericRegex([]string{"sendbird"}, hex8_4_4_4_12()), 33 | SecretGroup: 1, 34 | Keywords: []string{ 35 | "sendbird", 36 | }, 37 | } 38 | 39 | // validate 40 | tps := []string{ 41 | generateSampleSecret("sendbird", secrets.NewSecret(hex8_4_4_4_12())), 42 | } 43 | return validate(r, tps, nil) 44 | } 45 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/sumologic.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func SumoLogicAccessID() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "sumologic-access-id", 12 | Description: "SumoLogic Access ID", 13 | Regex: generateSemiGenericRegex([]string{"sumo"}, 14 | alphaNumeric("14")), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "sumo", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("sumo", secrets.NewSecret(alphaNumeric("14"))), 24 | } 25 | return validate(r, tps, nil) 26 | } 27 | 28 | func SumoLogicAccessToken() *config.Rule { 29 | // define rule 30 | r := config.Rule{ 31 | RuleID: "sumologic-access-token", 32 | Description: "SumoLogic Access Token", 33 | Regex: generateSemiGenericRegex([]string{"sumo"}, 34 | alphaNumeric("64")), 35 | SecretGroup: 1, 36 | Keywords: []string{ 37 | "sumo", 38 | }, 39 | } 40 | 41 | // validate 42 | tps := []string{ 43 | generateSampleSecret("sumo", secrets.NewSecret(alphaNumeric("64"))), 44 | } 45 | return validate(r, tps, nil) 46 | } 47 | -------------------------------------------------------------------------------- /report/finding.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Finding contains information about strings that 8 | // have been captured by a tree-sitter query. 9 | type Finding struct { 10 | Description string 11 | StartLine int 12 | EndLine int 13 | StartColumn int 14 | EndColumn int 15 | 16 | Line string `json:"-"` 17 | 18 | Match string 19 | 20 | // Secret contains the full content of what is matched in 21 | // the tree-sitter query. 22 | Secret string 23 | 24 | // File is the name of the file containing the finding 25 | File string 26 | SymlinkFile string 27 | Commit string 28 | 29 | // Entropy is the shannon entropy of Value 30 | Entropy float32 31 | 32 | Author string 33 | Email string 34 | Date string 35 | Message string 36 | Tags []string 37 | 38 | // Rule is the name of the rule that was matched 39 | RuleID string 40 | 41 | // unique identifier 42 | Fingerprint string 43 | } 44 | 45 | // Redact removes sensitive information from a finding. 46 | func (f *Finding) Redact() { 47 | f.Line = strings.Replace(f.Line, f.Secret, "REDACTED", -1) 48 | f.Match = strings.Replace(f.Match, f.Secret, "REDACTED", -1) 49 | f.Secret = "REDACTED" 50 | } 51 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/bitbucket.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func BitBucketClientID() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Bitbucket Client ID", 12 | RuleID: "bitbucket-client-id", 13 | Regex: generateSemiGenericRegex([]string{"bitbucket"}, alphaNumeric("32")), 14 | SecretGroup: 1, 15 | Keywords: []string{"bitbucket"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("bitbucket", secrets.NewSecret(alphaNumeric("32"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | 25 | func BitBucketClientSecret() *config.Rule { 26 | // define rule 27 | r := config.Rule{ 28 | Description: "Bitbucket Client Secret", 29 | RuleID: "bitbucket-client-secret", 30 | Regex: generateSemiGenericRegex([]string{"bitbucket"}, alphaNumericExtended("64")), 31 | SecretGroup: 1, 32 | Keywords: []string{"bitbucket"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("bitbucket", secrets.NewSecret(alphaNumeric("64"))), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | -------------------------------------------------------------------------------- /report/csv.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "encoding/csv" 5 | "io" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // writeCsv writes the list of findings to a writeCloser. 11 | func writeCsv(f []Finding, w io.WriteCloser) error { 12 | if len(f) == 0 { 13 | return nil 14 | } 15 | defer w.Close() 16 | cw := csv.NewWriter(w) 17 | err := cw.Write([]string{"RuleID", 18 | "Commit", 19 | "File", 20 | "SymlinkFile", 21 | "Secret", 22 | "Match", 23 | "StartLine", 24 | "EndLine", 25 | "StartColumn", 26 | "EndColumn", 27 | "Author", 28 | "Message", 29 | "Date", 30 | "Email", 31 | "Fingerprint", 32 | "Tags", 33 | }) 34 | if err != nil { 35 | return err 36 | } 37 | for _, f := range f { 38 | err = cw.Write([]string{f.RuleID, 39 | f.Commit, 40 | f.File, 41 | f.SymlinkFile, 42 | f.Secret, 43 | f.Match, 44 | strconv.Itoa(f.StartLine), 45 | strconv.Itoa(f.EndLine), 46 | strconv.Itoa(f.StartColumn), 47 | strconv.Itoa(f.EndColumn), 48 | f.Author, 49 | f.Message, 50 | f.Date, 51 | f.Email, 52 | f.Fingerprint, 53 | strings.Join(f.Tags, " "), 54 | }) 55 | if err != nil { 56 | return err 57 | } 58 | } 59 | 60 | cw.Flush() 61 | return cw.Error() 62 | } 63 | -------------------------------------------------------------------------------- /testdata/expected/git/small.txt: -------------------------------------------------------------------------------- 1 | import ( 2 | "fmt" 3 | "os" 4 | ) 5 | // seems safer 6 | aws_token := os.Getenv("AWS_TOKEN") 7 | package foo 8 | 9 | import "fmt" 10 | 11 | func Foo() { 12 | fmt.Println("foo") 13 | 14 | // seems safe 15 | aws_token := "AKIALALEMEL33243OLIA" 16 | fmt.Println(aws_token) 17 | } 18 | package api 19 | 20 | import "fmt" 21 | 22 | func PrintHello() { 23 | fmt.Println("hello") 24 | } 25 | import ( 26 | "fmt" 27 | "os" 28 | ) 29 | var a = "initial" 30 | fmt.Println(a) 31 | var b, c int = 1, 2 32 | fmt.Println(b, c) 33 | var d = true 34 | fmt.Println(d) 35 | var e int 36 | fmt.Println(e) 37 | // load secret via env 38 | awsToken := os.Getenv("AWS_TOKEN") 39 | 40 | f := "apple" 41 | fmt.Println(f) 42 | 43 | // opps I added a secret at line 20 44 | awsToken := "AKIALALEMEL33243OLIA" 45 | package main 46 | 47 | import "fmt" 48 | 49 | func main() { 50 | 51 | var a = "initial" 52 | fmt.Println(a) 53 | 54 | var b, c int = 1, 2 55 | fmt.Println(b, c) 56 | 57 | var d = true 58 | fmt.Println(d) 59 | 60 | var e int 61 | fmt.Println(e) 62 | 63 | f := "apple" 64 | fmt.Println(f) 65 | } 66 | # test 67 | This is a repo used for testing gitleaks 68 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/confluent.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func ConfluentSecretKey() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "confluent-secret-key", 12 | Description: "Confluent Secret Key", 13 | Regex: generateSemiGenericRegex([]string{"confluent"}, alphaNumeric("64")), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "confluent", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("confluent", secrets.NewSecret(alphaNumeric("64"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | 27 | func ConfluentAccessToken() *config.Rule { 28 | // define rule 29 | r := config.Rule{ 30 | RuleID: "confluent-access-token", 31 | Description: "Confluent Access Token", 32 | Regex: generateSemiGenericRegex([]string{"confluent"}, alphaNumeric("16")), 33 | SecretGroup: 1, 34 | Keywords: []string{ 35 | "confluent", 36 | }, 37 | } 38 | 39 | // validate 40 | tps := []string{ 41 | generateSampleSecret("confluent", secrets.NewSecret(alphaNumeric("16"))), 42 | } 43 | return validate(r, tps, nil) 44 | } 45 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/lob.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func LobPubAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Lob Publishable API Key", 12 | RuleID: "lob-pub-api-key", 13 | Regex: generateSemiGenericRegex([]string{"lob"}, `(test|live)_pub_[a-f0-9]{31}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "test_pub", 17 | "live_pub", 18 | "_pub", 19 | }, 20 | } 21 | 22 | // validate 23 | tps := []string{ 24 | generateSampleSecret("lob", "test_pub_"+secrets.NewSecret(hex("31"))), 25 | } 26 | return validate(r, tps, nil) 27 | } 28 | 29 | func LobAPIToken() *config.Rule { 30 | // define rule 31 | r := config.Rule{ 32 | Description: "Lob API Key", 33 | RuleID: "lob-api-key", 34 | Regex: generateSemiGenericRegex([]string{"lob"}, `(live|test)_[a-f0-9]{35}`), 35 | Keywords: []string{ 36 | "test_", 37 | "live_", 38 | }, 39 | SecretGroup: 1, 40 | } 41 | 42 | // validate 43 | tps := []string{ 44 | generateSampleSecret("lob", "test_"+secrets.NewSecret(hex("35"))), 45 | } 46 | return validate(r, tps, nil) 47 | } 48 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/linkedin.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func LinkedinClientSecret() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "linkedin-client-secret", 12 | Description: "LinkedIn Client secret", 13 | Regex: generateSemiGenericRegex([]string{ 14 | "linkedin", 15 | "linked-in", 16 | }, alphaNumeric("16")), 17 | SecretGroup: 1, 18 | Keywords: []string{ 19 | "linkedin", 20 | "linked-in", 21 | }, 22 | } 23 | 24 | // validate 25 | tps := []string{ 26 | generateSampleSecret("linkedin", secrets.NewSecret(alphaNumeric("16"))), 27 | } 28 | return validate(r, tps, nil) 29 | } 30 | 31 | func LinkedinClientID() *config.Rule { 32 | // define rule 33 | r := config.Rule{ 34 | RuleID: "linkedin-client-id", 35 | Description: "LinkedIn Client ID", 36 | Regex: generateSemiGenericRegex([]string{ 37 | "linkedin", 38 | "linked-in", 39 | }, alphaNumeric("14")), 40 | SecretGroup: 1, 41 | Keywords: []string{ 42 | "linkedin", 43 | "linked-in", 44 | }, 45 | } 46 | 47 | // validate 48 | tps := []string{ 49 | generateSampleSecret("linkedin", secrets.NewSecret(alphaNumeric("14"))), 50 | } 51 | return validate(r, tps, nil) 52 | } 53 | -------------------------------------------------------------------------------- /config/rule.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | // Rules contain information that define details on how to detect secrets 8 | type Rule struct { 9 | // Description is the description of the rule. 10 | Description string 11 | 12 | // RuleID is a unique identifier for this rule 13 | RuleID string 14 | 15 | // Entropy is a float representing the minimum shannon 16 | // entropy a regex group must have to be considered a secret. 17 | Entropy float64 18 | 19 | // SecretGroup is an int used to extract secret from regex 20 | // match and used as the group that will have its entropy 21 | // checked if `entropy` is set. 22 | SecretGroup int 23 | 24 | // Regex is a golang regular expression used to detect secrets. 25 | Regex *regexp.Regexp 26 | 27 | // Path is a golang regular expression used to 28 | // filter secrets by path 29 | Path *regexp.Regexp 30 | 31 | // Tags is an array of strings used for metadata 32 | // and reporting purposes. 33 | Tags []string 34 | 35 | // Keywords are used for pre-regex check filtering. Rules that contain 36 | // keywords will perform a quick string compare check to make sure the 37 | // keyword(s) are in the content being scanned. 38 | Keywords []string 39 | 40 | // Allowlist allows a rule to be ignored for specific 41 | // regexes, paths, and/or commits 42 | Allowlist Allowlist 43 | } 44 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/jwt.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/config" 5 | ) 6 | 7 | func JWT() *config.Rule { 8 | // define rule 9 | r := config.Rule{ 10 | Description: "JSON Web Token", 11 | RuleID: "jwt", 12 | Regex: generateUniqueTokenRegex(`ey[0-9a-z]{30,34}\.ey[0-9a-z-\/_]{30,500}\.[0-9a-zA-Z-\/_]{10,200}={0,2}`), 13 | Keywords: []string{"ey"}, 14 | } 15 | 16 | // validate 17 | tps := []string{`eyJhbGciOieeeiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwic3ViZSI6IjEyMzQ1Njc4OTAiLCJuYW1lZWEiOiJKb2huIERvZSIsInN1ZmV3YWZiIjoiMTIzNDU2Nzg5MCIsIm5hbWVmZWF3ZnciOiJKb2huIERvZSIsIm5hbWVhZmV3ZmEiOiJKb2huIERvZSIsInN1ZndhZndlYWIiOiIxMjM0NTY3ODkwIiwibmFtZWZ3YWYiOiJKb2huIERvZSIsInN1YmZ3YWYiOiIxMjM0NTY3ODkwIiwibmFtZndhZSI6IkpvaG4gRG9lIiwiaWZ3YWZhYXQiOjE1MTYyMzkwMjJ9.a_5icKBDo-8EjUlrfvz2k2k-FYaindQ0DEYNrlsnRG0==`, // gitleaks:allow 18 | `JWT := eyJhbGciOieeeiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwic3ViZSI6IjEyMzQ1Njc4OTAiLCJuYW1lZWEiOiJKb2huIERvZSIsInN1ZmV3YWZiIjoiMTIzNDU2Nzg5MCIsIm5hbWVmZWF3ZnciOiJKb2huIERvZSIsIm5hbWVhZmV3ZmEiOiJKb2huIERvZSIsInN1ZndhZndlYWIiOiIxMjM0NTY3ODkwIiwibmFtZWZ3YWYiOiJKb2huIERvZSIsInN1YmZ3YWYiOiIxMjM0NTY3ODkwIiwibmFtZndhZSI6IkpvaG4gRG9lIiwiaWZ3YWZhYXQiOjE1MTYyMzkwMjJ9.a_5icKBDo-8EjUlrfvz2k2k-FYaindQ0DEYNrlsnRG0`, // gitleaks:allow 19 | } 20 | return validate(r, tps, nil) 21 | } 22 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/generic.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/config" 5 | ) 6 | 7 | func GenericCredential() *config.Rule { 8 | // define rule 9 | r := config.Rule{ 10 | RuleID: "generic-api-key", 11 | Description: "Generic API Key", 12 | Regex: generateSemiGenericRegex([]string{ 13 | "key", 14 | "api", 15 | "token", 16 | "secret", 17 | "client", 18 | "passwd", 19 | "password", 20 | "auth", 21 | "access", 22 | }, `[0-9a-z\-_.=]{10,150}`), 23 | SecretGroup: 1, 24 | Keywords: []string{ 25 | "key", 26 | "api", 27 | "token", 28 | "secret", 29 | "client", 30 | "passwd", 31 | "password", 32 | "auth", 33 | "access", 34 | }, 35 | Entropy: 3.5, 36 | Allowlist: config.Allowlist{ 37 | StopWords: DefaultStopWords, 38 | }, 39 | } 40 | 41 | // validate 42 | tps := []string{ 43 | generateSampleSecret("generic", "CLOJARS_34bf0e88955ff5a1c328d6a7491acc4f48e865a7b8dd4d70a70749037443"), 44 | generateSampleSecret("generic", "Zf3D0LXCM3EIMbgJpUNnkRtOfOueHznB"), 45 | `"client_id" : "0afae57f3ccfd9d7f5767067bc48b30f719e271ba470488056e37ab35d4b6506"`, 46 | `"client_secret" : "6da89121079f83b2eb6acccf8219ea982c3d79bccc3e9c6a85856480661f8fde",`, 47 | } 48 | fps := []string{ 49 | `client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.client-vpn-endpoint.id`, 50 | `password combination. 51 | 52 | R5: Regulatory--21`, 53 | } 54 | return validate(r, tps, fps) 55 | } 56 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/dropbox.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func DropBoxAPISecret() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Dropbox API secret", 12 | RuleID: "dropbox-api-token", 13 | Regex: generateSemiGenericRegex([]string{"dropbox"}, alphaNumeric("15")), 14 | SecretGroup: 1, 15 | Keywords: []string{"dropbox"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("dropbox", secrets.NewSecret(alphaNumeric("15"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | 25 | func DropBoxShortLivedAPIToken() *config.Rule { 26 | // define rule 27 | r := config.Rule{ 28 | RuleID: "dropbox-short-lived-api-token", 29 | Description: "Dropbox short lived API token", 30 | Regex: generateSemiGenericRegex([]string{"dropbox"}, `sl\.[a-z0-9\-=_]{135}`), 31 | Keywords: []string{"dropbox"}, 32 | } 33 | 34 | // validate TODO 35 | return &r 36 | } 37 | 38 | func DropBoxLongLivedAPIToken() *config.Rule { 39 | // define rule 40 | r := config.Rule{ 41 | RuleID: "dropbox-long-lived-api-token", 42 | Description: "Dropbox long lived API token", 43 | Regex: generateSemiGenericRegex([]string{"dropbox"}, `[a-z0-9]{11}(AAAAAAAAAA)[a-z0-9\-_=]{43}`), 44 | Keywords: []string{"dropbox"}, 45 | } 46 | 47 | // validate TODO 48 | return &r 49 | } 50 | -------------------------------------------------------------------------------- /detect/location_test.go: -------------------------------------------------------------------------------- 1 | package detect 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // TestGetLocation tests the getLocation function. 8 | func TestGetLocation(t *testing.T) { 9 | tests := []struct { 10 | linePairs [][]int 11 | start int 12 | end int 13 | wantLocation Location 14 | }{ 15 | { 16 | linePairs: [][]int{ 17 | {0, 39}, 18 | {40, 55}, 19 | {56, 57}, 20 | }, 21 | start: 35, 22 | end: 38, 23 | wantLocation: Location{ 24 | startLine: 1, 25 | startColumn: 36, 26 | endLine: 1, 27 | endColumn: 38, 28 | startLineIndex: 0, 29 | endLineIndex: 40, 30 | }, 31 | }, 32 | { 33 | linePairs: [][]int{ 34 | {0, 39}, 35 | {40, 55}, 36 | {56, 57}, 37 | }, 38 | start: 40, 39 | end: 44, 40 | wantLocation: Location{ 41 | startLine: 2, 42 | startColumn: 1, 43 | endLine: 2, 44 | endColumn: 4, 45 | startLineIndex: 40, 46 | endLineIndex: 56, 47 | }, 48 | }, 49 | } 50 | 51 | for _, test := range tests { 52 | loc := location(Fragment{newlineIndices: test.linePairs}, []int{test.start, test.end}) 53 | if loc != test.wantLocation { 54 | t.Errorf("\nstartLine %d\nstartColumn: %d\nendLine: %d\nendColumn: %d\nstartLineIndex: %d\nendlineIndex %d", 55 | loc.startLine, loc.startColumn, loc.endLine, loc.endColumn, loc.startLineIndex, loc.endLineIndex) 56 | 57 | t.Error("got", loc, "want", test.wantLocation) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /testdata/baseline/baseline.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Description": "PyPI upload token", 4 | "StartLine": 32, 5 | "EndLine": 32, 6 | "StartColumn": 21, 7 | "EndColumn": 106, 8 | "Match": "************************", 9 | "Secret": "************************", 10 | "File": "detect/detect_test.go", 11 | "Commit": "9326f35380636bcbe61e94b0584d1618c4b5c2c2", 12 | "Entropy": 1.9606875, 13 | "Author": "****", 14 | "Email": "****", 15 | "Date": "2022-03-07T14:33:06Z", 16 | "Message": "Escape - character in regex character groups (#802)\n\n* fix char escape\n\n* add test\n\n* fix verbosity in make test", 17 | "Tags": [], 18 | "RuleID": "pypi-upload-token", 19 | "Fingerprint": "9326f35380636bcbe61e94b0584d1618c4b5c2c2:detect/detect_test.go:pypi-upload-token:32" 20 | }, 21 | { 22 | "Description": "PyPI upload token", 23 | "StartLine": 33, 24 | "EndLine": 33, 25 | "StartColumn": 21, 26 | "EndColumn": 106, 27 | "Match": "************************", 28 | "Secret": "************************", 29 | "File": "detect/detect_test.go", 30 | "Commit": "9326f35380636bcbe61e94b0584d1618c4b5c2c2", 31 | "Entropy": 1.9606875, 32 | "Author": "****", 33 | "Email": "****", 34 | "Date": "2022-03-07T14:33:06Z", 35 | "Message": "Escape - character in regex character groups (#802)\n\n* fix char escape\n\n* add test\n\n* fix verbosity in make test", 36 | "Tags": [], 37 | "RuleID": "pypi-upload-token", 38 | "Fingerprint": "9326f35380636bcbe61e94b0584d1618c4b5c2c2:detect/detect_test.go:pypi-upload-token:33" 39 | } 40 | ] 41 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/digitalocean.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func DigitalOceanPAT() *config.Rule { 9 | r := config.Rule{ 10 | Description: "DigitalOcean Personal Access Token", 11 | RuleID: "digitalocean-pat", 12 | SecretGroup: 1, 13 | Regex: generateUniqueTokenRegex(`dop_v1_[a-f0-9]{64}`), 14 | Keywords: []string{"dop_v1_"}, 15 | } 16 | 17 | tps := []string{ 18 | generateSampleSecret("do", "dop_v1_"+secrets.NewSecret(hex("64"))), 19 | } 20 | return validate(r, tps, nil) 21 | } 22 | 23 | func DigitalOceanOAuthToken() *config.Rule { 24 | r := config.Rule{ 25 | Description: "DigitalOcean OAuth Access Token", 26 | RuleID: "digitalocean-access-token", 27 | SecretGroup: 1, 28 | Regex: generateUniqueTokenRegex(`doo_v1_[a-f0-9]{64}`), 29 | Keywords: []string{"doo_v1_"}, 30 | } 31 | 32 | tps := []string{ 33 | generateSampleSecret("do", "doo_v1_"+secrets.NewSecret(hex("64"))), 34 | } 35 | return validate(r, tps, nil) 36 | } 37 | 38 | func DigitalOceanRefreshToken() *config.Rule { 39 | r := config.Rule{ 40 | Description: "DigitalOcean OAuth Refresh Token", 41 | RuleID: "digitalocean-refresh-token", 42 | SecretGroup: 1, 43 | Regex: generateUniqueTokenRegex(`dor_v1_[a-f0-9]{64}`), 44 | Keywords: []string{"dor_v1_"}, 45 | } 46 | 47 | tps := []string{ 48 | generateSampleSecret("do", "dor_v1_"+secrets.NewSecret(hex("64"))), 49 | } 50 | return validate(r, tps, nil) 51 | } 52 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/gitlab.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func GitlabPat() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "GitLab Personal Access Token", 14 | RuleID: "gitlab-pat", 15 | Regex: regexp.MustCompile(`glpat-[0-9a-zA-Z\-\_]{20}`), 16 | Keywords: []string{"glpat-"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("gitlab", "glpat-"+secrets.NewSecret(alphaNumeric("20"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | 26 | func GitlabPipelineTriggerToken() *config.Rule { 27 | // define rule 28 | r := config.Rule{ 29 | Description: "GitLab Pipeline Trigger Token", 30 | RuleID: "gitlab-ptt", 31 | Regex: regexp.MustCompile(`glptt-[0-9a-f]{40}`), 32 | Keywords: []string{"glptt-"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("gitlab", "glptt-"+secrets.NewSecret(hex("40"))), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | 42 | func GitlabRunnerRegistrationToken() *config.Rule { 43 | // define rule 44 | r := config.Rule{ 45 | Description: "GitLab Runner Registration Token", 46 | RuleID: "gitlab-rrt", 47 | Regex: regexp.MustCompile(`GR1348941[0-9a-zA-Z\-\_]{20}`), 48 | Keywords: []string{"GR1348941"}, 49 | } 50 | 51 | // validate 52 | tps := []string{ 53 | generateSampleSecret("gitlab", "GR1348941"+secrets.NewSecret(alphaNumeric("20"))), 54 | } 55 | return validate(r, tps, nil) 56 | } 57 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/messagebird.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func MessageBirdAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "MessageBird API token", 12 | RuleID: "messagebird-api-token", 13 | Regex: generateSemiGenericRegex([]string{ 14 | "messagebird", 15 | "message-bird", 16 | "message_bird", 17 | }, alphaNumeric("25")), 18 | SecretGroup: 1, 19 | Keywords: []string{ 20 | "messagebird", 21 | "message-bird", 22 | "message_bird", 23 | }, 24 | } 25 | 26 | // validate 27 | tps := []string{ 28 | generateSampleSecret("messagebird", secrets.NewSecret(alphaNumeric("25"))), 29 | generateSampleSecret("message-bird", secrets.NewSecret(alphaNumeric("25"))), 30 | generateSampleSecret("message_bird", secrets.NewSecret(alphaNumeric("25"))), 31 | } 32 | return validate(r, tps, nil) 33 | } 34 | 35 | func MessageBirdClientID() *config.Rule { 36 | // define rule 37 | r := config.Rule{ 38 | Description: "MessageBird client ID", 39 | RuleID: "messagebird-client-id", 40 | Regex: generateSemiGenericRegex([]string{ 41 | "messagebird", 42 | "message-bird", 43 | "message_bird", 44 | }, hex8_4_4_4_12()), 45 | SecretGroup: 1, 46 | Keywords: []string{ 47 | "messagebird", 48 | "message-bird", 49 | "message_bird", 50 | }, 51 | } 52 | 53 | // validate 54 | tps := []string{ 55 | `const MessageBirdClientID = "12345678-ABCD-ABCD-ABCD-1234567890AB"`, // gitleaks:allow 56 | } 57 | return validate(r, tps, nil) 58 | } 59 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/discord.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func DiscordAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Discord API key", 12 | RuleID: "discord-api-token", 13 | Regex: generateSemiGenericRegex([]string{"discord"}, hex("64")), 14 | SecretGroup: 1, 15 | Keywords: []string{"discord"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("discord", secrets.NewSecret(hex("64"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | 25 | func DiscordClientID() *config.Rule { 26 | // define rule 27 | r := config.Rule{ 28 | Description: "Discord client ID", 29 | RuleID: "discord-client-id", 30 | Regex: generateSemiGenericRegex([]string{"discord"}, numeric("18")), 31 | SecretGroup: 1, 32 | Keywords: []string{"discord"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("discord", secrets.NewSecret(numeric("18"))), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | 42 | func DiscordClientSecret() *config.Rule { 43 | // define rule 44 | r := config.Rule{ 45 | Description: "Discord client secret", 46 | RuleID: "discord-client-secret", 47 | Regex: generateSemiGenericRegex([]string{"discord"}, alphaNumericExtended("32")), 48 | SecretGroup: 1, 49 | Keywords: []string{"discord"}, 50 | } 51 | 52 | // validate 53 | tps := []string{ 54 | generateSampleSecret("discord", secrets.NewSecret(numeric("32"))), 55 | } 56 | return validate(r, tps, nil) 57 | } 58 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/flutterwave.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func FlutterwavePublicKey() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Finicity Public Key", 14 | RuleID: "flutterwave-public-key", 15 | Regex: regexp.MustCompile(`FLWPUBK_TEST-(?i)[a-h0-9]{32}-X`), 16 | Keywords: []string{"FLWPUBK_TEST"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("flutterwavePubKey", "FLWPUBK_TEST-"+secrets.NewSecret(hex("32"))+"-X"), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | 26 | func FlutterwaveSecretKey() *config.Rule { 27 | // define rule 28 | r := config.Rule{ 29 | Description: "Flutterwave Secret Key", 30 | RuleID: "flutterwave-secret-key", 31 | Regex: regexp.MustCompile(`FLWSECK_TEST-(?i)[a-h0-9]{32}-X`), 32 | Keywords: []string{"FLWSECK_TEST"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("flutterwavePubKey", "FLWSECK_TEST-"+secrets.NewSecret(hex("32"))+"-X"), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | 42 | func FlutterwaveEncKey() *config.Rule { 43 | // define rule 44 | r := config.Rule{ 45 | Description: "Flutterwave Encryption Key", 46 | RuleID: "flutterwave-encryption-key", 47 | Regex: regexp.MustCompile(`FLWSECK_TEST-(?i)[a-h0-9]{12}`), 48 | Keywords: []string{"FLWSECK_TEST"}, 49 | } 50 | 51 | // validate 52 | tps := []string{ 53 | generateSampleSecret("flutterwavePubKey", "FLWSECK_TEST-"+secrets.NewSecret(hex("12"))), 54 | } 55 | return validate(r, tps, nil) 56 | } 57 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/mailgun.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func MailGunPrivateAPIToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "mailgun-private-api-token", 12 | Description: "Mailgun private API token", 13 | Regex: generateSemiGenericRegex([]string{"mailgun"}, `key-[a-f0-9]{32}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "mailgun", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("mailgun", "key-"+secrets.NewSecret(hex("32"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | 27 | func MailGunPubAPIToken() *config.Rule { 28 | // define rule 29 | r := config.Rule{ 30 | RuleID: "mailgun-pub-key", 31 | Description: "Mailgun public validation key", 32 | Regex: generateSemiGenericRegex([]string{"mailgun"}, `pubkey-[a-f0-9]{32}`), 33 | SecretGroup: 1, 34 | Keywords: []string{ 35 | "mailgun", 36 | }, 37 | } 38 | 39 | // validate 40 | tps := []string{ 41 | generateSampleSecret("mailgun", "pubkey-"+secrets.NewSecret(hex("32"))), 42 | } 43 | return validate(r, tps, nil) 44 | } 45 | 46 | func MailGunSigningKey() *config.Rule { 47 | // define rule 48 | r := config.Rule{ 49 | RuleID: "mailgun-signing-key", 50 | Description: "Mailgun webhook signing key", 51 | Regex: generateSemiGenericRegex([]string{"mailgun"}, `[a-h0-9]{32}-[a-h0-9]{8}-[a-h0-9]{8}`), 52 | SecretGroup: 1, 53 | Keywords: []string{ 54 | "mailgun", 55 | }, 56 | } 57 | 58 | // validate 59 | tps := []string{ 60 | generateSampleSecret("mailgun", secrets.NewSecret(hex("32"))+"-00001111-22223333"), 61 | } 62 | return validate(r, tps, nil) 63 | } 64 | -------------------------------------------------------------------------------- /config/allowlist.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | // Allowlist allows a rule to be ignored for specific 9 | // regexes, paths, and/or commits 10 | type Allowlist struct { 11 | // Short human readable description of the allowlist. 12 | Description string 13 | 14 | // Regexes is slice of content regular expressions that are allowed to be ignored. 15 | Regexes []*regexp.Regexp 16 | 17 | // RegexTarget 18 | RegexTarget string 19 | 20 | // Paths is a slice of path regular expressions that are allowed to be ignored. 21 | Paths []*regexp.Regexp 22 | 23 | // Commits is a slice of commit SHAs that are allowed to be ignored. 24 | Commits []string 25 | 26 | // StopWords is a slice of stop words that are allowed to be ignored. 27 | // This targets the _secret_, not the content of the regex match like the 28 | // Regexes slice. 29 | StopWords []string 30 | } 31 | 32 | // CommitAllowed returns true if the commit is allowed to be ignored. 33 | func (a *Allowlist) CommitAllowed(c string) bool { 34 | if c == "" { 35 | return false 36 | } 37 | for _, commit := range a.Commits { 38 | if commit == c { 39 | return true 40 | } 41 | } 42 | return false 43 | } 44 | 45 | // PathAllowed returns true if the path is allowed to be ignored. 46 | func (a *Allowlist) PathAllowed(path string) bool { 47 | return anyRegexMatch(path, a.Paths) 48 | } 49 | 50 | // RegexAllowed returns true if the regex is allowed to be ignored. 51 | func (a *Allowlist) RegexAllowed(s string) bool { 52 | return anyRegexMatch(s, a.Regexes) 53 | } 54 | 55 | func (a *Allowlist) ContainsStopWord(s string) bool { 56 | s = strings.ToLower(s) 57 | for _, stopWord := range a.StopWords { 58 | if strings.Contains(s, strings.ToLower(stopWord)) { 59 | return true 60 | } 61 | } 62 | return false 63 | } 64 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/plaid.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func PlaidAccessID() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | RuleID: "plaid-client-id", 14 | Description: "Plaid Client ID", 15 | Regex: generateSemiGenericRegex([]string{"plaid"}, alphaNumeric("24")), 16 | SecretGroup: 1, 17 | Keywords: []string{ 18 | "plaid", 19 | }, 20 | } 21 | 22 | // validate 23 | tps := []string{ 24 | generateSampleSecret("plaid", secrets.NewSecret(alphaNumeric("24"))), 25 | } 26 | return validate(r, tps, nil) 27 | } 28 | 29 | func PlaidSecretKey() *config.Rule { 30 | // define rule 31 | r := config.Rule{ 32 | RuleID: "plaid-secret-key", 33 | Description: "Plaid Secret key", 34 | Regex: generateSemiGenericRegex([]string{"plaid"}, alphaNumeric("30")), 35 | SecretGroup: 1, 36 | Keywords: []string{ 37 | "plaid", 38 | }, 39 | } 40 | 41 | // validate 42 | tps := []string{ 43 | generateSampleSecret("plaid", secrets.NewSecret(alphaNumeric("30"))), 44 | } 45 | return validate(r, tps, nil) 46 | } 47 | 48 | func PlaidAccessToken() *config.Rule { 49 | // define rule 50 | r := config.Rule{ 51 | RuleID: "plaid-api-token", 52 | Description: "Plaid API Token", 53 | Regex: generateSemiGenericRegex([]string{"plaid"}, 54 | fmt.Sprintf("access-(?:sandbox|development|production)-%s", hex8_4_4_4_12())), 55 | SecretGroup: 1, 56 | Keywords: []string{ 57 | "plaid", 58 | }, 59 | } 60 | 61 | // validate 62 | tps := []string{ 63 | generateSampleSecret("plaid", secrets.NewSecret(fmt.Sprintf("access-(?:sandbox|development|production)-%s", hex8_4_4_4_12()))), 64 | } 65 | return validate(r, tps, nil) 66 | } 67 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/yandex.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func YandexAWSAccessToken() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "yandex-aws-access-token", 12 | Description: "Yandex AWS Access Token", 13 | Regex: generateSemiGenericRegex([]string{"yandex"}, 14 | `YC[a-zA-Z0-9_\-]{38}`), 15 | SecretGroup: 1, 16 | Keywords: []string{ 17 | "yandex", 18 | }, 19 | } 20 | 21 | // validate 22 | tps := []string{ 23 | generateSampleSecret("yandex", 24 | secrets.NewSecret(`YC[a-zA-Z0-9_\-]{38}`)), 25 | } 26 | return validate(r, tps, nil) 27 | } 28 | 29 | func YandexAPIKey() *config.Rule { 30 | // define rule 31 | r := config.Rule{ 32 | RuleID: "yandex-api-key", 33 | Description: "Yandex API Key", 34 | Regex: generateSemiGenericRegex([]string{"yandex"}, 35 | `AQVN[A-Za-z0-9_\-]{35,38}`), 36 | SecretGroup: 1, 37 | Keywords: []string{ 38 | "yandex", 39 | }, 40 | } 41 | 42 | // validate 43 | tps := []string{ 44 | generateSampleSecret("yandex", 45 | secrets.NewSecret(`AQVN[A-Za-z0-9_\-]{35,38}`)), 46 | } 47 | return validate(r, tps, nil) 48 | } 49 | 50 | func YandexAccessToken() *config.Rule { 51 | // define rule 52 | r := config.Rule{ 53 | RuleID: "yandex-access-token", 54 | Description: "Yandex Access Token", 55 | Regex: generateSemiGenericRegex([]string{"yandex"}, 56 | `t1\.[A-Z0-9a-z_-]+[=]{0,2}\.[A-Z0-9a-z_-]{86}[=]{0,2}`), 57 | SecretGroup: 1, 58 | Keywords: []string{ 59 | "yandex", 60 | }, 61 | } 62 | 63 | // validate 64 | tps := []string{ 65 | generateSampleSecret("yandex", 66 | secrets.NewSecret(`t1\.[A-Z0-9a-z_-]+[=]{0,2}\.[A-Z0-9a-z_-]{86}[=]{0,2}`)), 67 | } 68 | return validate(r, tps, nil) 69 | } 70 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/grafana.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func GrafanaApiKey() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Grafana api key (or Grafana cloud api key)", 12 | RuleID: "grafana-api-key", 13 | SecretGroup: 1, 14 | Regex: generateUniqueTokenRegex(`eyJrIjoi[A-Za-z0-9]{70,400}={0,2}`), 15 | Keywords: []string{"eyJrIjoi"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("grafana-api-key", 21 | "eyJrIjoi"+ 22 | secrets.NewSecret(alphaNumeric("70"))), 23 | } 24 | return validate(r, tps, nil) 25 | } 26 | 27 | func GrafanaCloudApiToken() *config.Rule { 28 | // define rule 29 | r := config.Rule{ 30 | Description: "Grafana cloud api token", 31 | RuleID: "grafana-cloud-api-token", 32 | SecretGroup: 1, 33 | Regex: generateUniqueTokenRegex(`glc_[A-Za-z0-9+/]{32,400}={0,2}`), 34 | Keywords: []string{"glc_"}, 35 | } 36 | 37 | // validate 38 | tps := []string{ 39 | generateSampleSecret("grafana-cloud-api-token", 40 | "glc_"+ 41 | secrets.NewSecret(alphaNumeric("32"))), 42 | } 43 | return validate(r, tps, nil) 44 | } 45 | 46 | func GrafanaServiceAccountToken() *config.Rule { 47 | // define rule 48 | r := config.Rule{ 49 | Description: "Grafana service account token", 50 | RuleID: "grafana-service-account-token", 51 | SecretGroup: 1, 52 | Regex: generateUniqueTokenRegex(`glsa_[A-Za-z0-9]{32}_[A-Fa-f0-9]{8}`), 53 | Keywords: []string{"glsa_"}, 54 | } 55 | 56 | // validate 57 | tps := []string{ 58 | generateSampleSecret("grafana-service-account-token", 59 | "glsa_"+ 60 | secrets.NewSecret(alphaNumeric("32"))+ 61 | "_"+ 62 | secrets.NewSecret((hex("8")))), 63 | } 64 | return validate(r, tps, nil) 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create and publish a Docker image 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | env: 8 | REGISTRY: ghcr.io 9 | IMAGE_NAME: ${{ github.repository }} 10 | 11 | jobs: 12 | build-and-push-image: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v2 21 | 22 | - name: Set up QEMU 23 | uses: docker/setup-qemu-action@8b122486cedac8393e77aa9734c3528886e4a1a8 24 | 25 | - name: Set up Docker Buildx 26 | id: buildx 27 | uses: docker/setup-buildx-action@dc7b9719a96d48369863986a06765841d7ea23f6 28 | 29 | - name: Log in to Docker Hub 30 | uses: docker/login-action@49ed152c8eca782a232dede0303416e8f356c37b 31 | with: 32 | username: ${{ github.actor }} 33 | password: ${{ secrets.DOCKER_PASSWORD }} 34 | 35 | - name: Log in to the Container registry 36 | uses: docker/login-action@49ed152c8eca782a232dede0303416e8f356c37b 37 | with: 38 | registry: ${{ env.REGISTRY }} 39 | username: ${{ github.actor }} 40 | password: ${{ secrets.GITHUB_TOKEN }} 41 | 42 | - name: Extract metadata (tags, labels) for Docker 43 | id: meta 44 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 45 | with: 46 | images: | 47 | zricethezav/gitleaks 48 | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 49 | 50 | - name: Build and push Docker image 51 | uses: docker/build-push-action@e551b19e49efd4e98792db7592c17c09b89db8d8 52 | with: 53 | platforms: linux/amd64,linux/arm64 54 | context: . 55 | push: true 56 | tags: ${{ steps.meta.outputs.tags }} 57 | labels: ${{ steps.meta.outputs.labels }} 58 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/telegram.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func TelegramBotToken() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Telegram Bot API Token", 14 | RuleID: "telegram-bot-api-token", 15 | SecretGroup: 1, 16 | Regex: regexp.MustCompile(`(?i)(?:^|[^0-9])([0-9]{5,16}:A[a-zA-Z0-9_\-]{34})(?:$|[^a-zA-Z0-9_\-])`), 17 | Keywords: []string{ 18 | "telegram", 19 | "api", 20 | "bot", 21 | "token", 22 | "url", 23 | }, 24 | } 25 | 26 | // validate 27 | validToken := secrets.NewSecret(numeric("8") + ":A" + alphaNumericExtendedShort("34")) 28 | minToken := secrets.NewSecret(numeric("5") + ":A" + alphaNumericExtendedShort("34")) 29 | maxToken := secrets.NewSecret(numeric("16") + ":A" + alphaNumericExtendedShort("34")) 30 | tps := []string{ 31 | // variable assignment 32 | generateSampleSecret("telegram", validToken), 33 | // URL containing token 34 | generateSampleSecret("url", "https://api.telegram.org/bot"+validToken+"/sendMessage"), 35 | // object constructor 36 | `const bot = new Telegraf("` + validToken + `")`, 37 | // .env 38 | `API_TOKEN = ` + validToken, 39 | // YAML 40 | `bot: ` + validToken, 41 | // Token with min bot_id 42 | generateSampleSecret("telegram", minToken), 43 | // Token with max bot_id 44 | generateSampleSecret("telegram", maxToken), 45 | } 46 | 47 | tooSmallToken := secrets.NewSecret(numeric("4") + ":A" + alphaNumericExtendedShort("34")) 48 | tooBigToken := secrets.NewSecret(numeric("17") + ":A" + alphaNumericExtendedShort("34")) 49 | fps := []string{ 50 | // Token with too small bot_id 51 | generateSampleSecret("telegram", tooSmallToken), 52 | // Token with too big bot_id 53 | generateSampleSecret("telegram", tooBigToken), 54 | } 55 | 56 | return validate(r, tps, fps) 57 | } 58 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/newrelic.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func NewRelicUserID() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "new-relic-user-api-key", 12 | Description: "New Relic user API Key", 13 | Regex: generateSemiGenericRegex([]string{ 14 | "new-relic", 15 | "newrelic", 16 | "new_relic", 17 | }, `NRAK-[a-z0-9]{27}`), 18 | SecretGroup: 1, 19 | Keywords: []string{ 20 | "NRAK", 21 | }, 22 | } 23 | 24 | // validate 25 | tps := []string{ 26 | generateSampleSecret("new-relic", "NRAK-"+secrets.NewSecret(alphaNumeric("27"))), 27 | } 28 | return validate(r, tps, nil) 29 | } 30 | 31 | func NewRelicUserKey() *config.Rule { 32 | // define rule 33 | r := config.Rule{ 34 | RuleID: "new-relic-user-api-id", 35 | Description: "New Relic user API ID", 36 | Regex: generateSemiGenericRegex([]string{ 37 | "new-relic", 38 | "newrelic", 39 | "new_relic", 40 | }, alphaNumeric("64")), 41 | SecretGroup: 1, 42 | Keywords: []string{ 43 | "new-relic", 44 | "newrelic", 45 | "new_relic", 46 | }, 47 | } 48 | 49 | // validate 50 | tps := []string{ 51 | generateSampleSecret("new-relic", secrets.NewSecret(alphaNumeric("64"))), 52 | } 53 | return validate(r, tps, nil) 54 | } 55 | 56 | func NewRelicBrowserAPIKey() *config.Rule { 57 | // define rule 58 | r := config.Rule{ 59 | RuleID: "new-relic-browser-api-token", 60 | Description: "New Relic ingest browser API token", 61 | Regex: generateSemiGenericRegex([]string{ 62 | "new-relic", 63 | "newrelic", 64 | "new_relic", 65 | }, `NRJS-[a-f0-9]{19}`), 66 | SecretGroup: 1, 67 | Keywords: []string{ 68 | "NRJS-", 69 | }, 70 | } 71 | 72 | // validate 73 | tps := []string{ 74 | generateSampleSecret("new-relic", "NRJS-"+secrets.NewSecret(hex("19"))), 75 | } 76 | return validate(r, tps, nil) 77 | } 78 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zricethezav/gitleaks/v8 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/charmbracelet/lipgloss v0.5.0 7 | github.com/fatih/semgroup v1.2.0 8 | github.com/gitleaks/go-gitdiff v0.8.0 9 | github.com/h2non/filetype v1.1.3 10 | github.com/rs/zerolog v1.26.1 11 | github.com/spf13/cobra v1.2.1 12 | github.com/spf13/viper v1.8.1 13 | github.com/stretchr/testify v1.7.0 14 | ) 15 | 16 | require ( 17 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 18 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 19 | github.com/mattn/go-isatty v0.0.17 // indirect 20 | github.com/mattn/go-runewidth v0.0.14 // indirect 21 | github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 // indirect 22 | github.com/muesli/termenv v0.15.1 // indirect 23 | github.com/rivo/uniseg v0.2.0 // indirect 24 | ) 25 | 26 | require ( 27 | github.com/davecgh/go-spew v1.1.1 // indirect 28 | github.com/fsnotify/fsnotify v1.4.9 // indirect 29 | github.com/hashicorp/hcl v1.0.0 // indirect 30 | github.com/inconshreveable/mousetrap v1.0.0 // indirect 31 | github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb 32 | github.com/magiconair/properties v1.8.5 // indirect 33 | github.com/mitchellh/mapstructure v1.4.1 // indirect 34 | github.com/pelletier/go-toml v1.9.3 // indirect 35 | github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 36 | github.com/pmezard/go-difflib v1.0.0 // indirect 37 | github.com/spf13/afero v1.6.0 // indirect 38 | github.com/spf13/cast v1.3.1 // indirect 39 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 40 | github.com/spf13/pflag v1.0.5 // indirect 41 | github.com/subosito/gotenv v1.2.0 // indirect 42 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect 43 | golang.org/x/sys v0.6.0 // indirect 44 | golang.org/x/text v0.3.6 // indirect 45 | gopkg.in/ini.v1 v1.62.0 // indirect 46 | gopkg.in/yaml.v2 v2.4.0 // indirect 47 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect 48 | ) 49 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/shopify.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func ShopifySharedSecret() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "Shopify shared secret", 14 | RuleID: "shopify-shared-secret", 15 | Regex: regexp.MustCompile(`shpss_[a-fA-F0-9]{32}`), 16 | Keywords: []string{"shpss_"}, 17 | } 18 | 19 | // validate 20 | tps := []string{"shopifySecret := \"shpss_" + secrets.NewSecret(hex("32")) + "\""} 21 | return validate(r, tps, nil) 22 | } 23 | 24 | func ShopifyAccessToken() *config.Rule { 25 | // define rule 26 | r := config.Rule{ 27 | Description: "Shopify access token", 28 | RuleID: "shopify-access-token", 29 | Regex: regexp.MustCompile(`shpat_[a-fA-F0-9]{32}`), 30 | Keywords: []string{"shpat_"}, 31 | } 32 | 33 | // validate 34 | tps := []string{"shopifyToken := \"shpat_" + secrets.NewSecret(hex("32")) + "\""} 35 | return validate(r, tps, nil) 36 | } 37 | 38 | func ShopifyCustomAccessToken() *config.Rule { 39 | // define rule 40 | r := config.Rule{ 41 | Description: "Shopify custom access token", 42 | RuleID: "shopify-custom-access-token", 43 | Regex: regexp.MustCompile(`shpca_[a-fA-F0-9]{32}`), 44 | Keywords: []string{"shpca_"}, 45 | } 46 | 47 | // validate 48 | tps := []string{"shopifyToken := \"shpca_" + secrets.NewSecret(hex("32")) + "\""} 49 | return validate(r, tps, nil) 50 | } 51 | 52 | func ShopifyPrivateAppAccessToken() *config.Rule { 53 | // define rule 54 | r := config.Rule{ 55 | Description: "Shopify private app access token", 56 | RuleID: "shopify-private-app-access-token", 57 | Regex: regexp.MustCompile(`shppa_[a-fA-F0-9]{32}`), 58 | Keywords: []string{"shppa_"}, 59 | } 60 | 61 | // validate 62 | tps := []string{"shopifyToken := \"shppa_" + secrets.NewSecret(hex("32")) + "\""} 63 | return validate(r, tps, nil) 64 | } 65 | -------------------------------------------------------------------------------- /detect/baseline.go: -------------------------------------------------------------------------------- 1 | package detect 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "os" 8 | 9 | "github.com/rs/zerolog/log" 10 | 11 | "github.com/zricethezav/gitleaks/v8/report" 12 | ) 13 | 14 | func IsNew(finding report.Finding, baseline []report.Finding) bool { 15 | // Explicitly testing each property as it gives significantly better performance in comparison to cmp.Equal(). Drawback is that 16 | // the code requires maintenance if/when the Finding struct changes 17 | for _, b := range baseline { 18 | 19 | if finding.Author == b.Author && 20 | finding.Commit == b.Commit && 21 | finding.Date == b.Date && 22 | finding.Description == b.Description && 23 | finding.Email == b.Email && 24 | finding.EndColumn == b.EndColumn && 25 | finding.EndLine == b.EndLine && 26 | finding.Entropy == b.Entropy && 27 | finding.File == b.File && 28 | // Omit checking finding.Fingerprint - if the format of the fingerprint changes, the users will see unexpected behaviour 29 | finding.Match == b.Match && 30 | finding.Message == b.Message && 31 | finding.RuleID == b.RuleID && 32 | finding.Secret == b.Secret && 33 | finding.StartColumn == b.StartColumn && 34 | finding.StartLine == b.StartLine { 35 | return false 36 | } 37 | } 38 | return true 39 | } 40 | 41 | func LoadBaseline(baselinePath string) ([]report.Finding, error) { 42 | var previousFindings []report.Finding 43 | jsonFile, err := os.Open(baselinePath) 44 | if err != nil { 45 | return nil, fmt.Errorf("could not open %s", baselinePath) 46 | } 47 | 48 | defer func() { 49 | if cerr := jsonFile.Close(); cerr != nil { 50 | log.Warn().Err(cerr).Msg("problem closing jsonFile handle") 51 | } 52 | }() 53 | 54 | bytes, err := io.ReadAll(jsonFile) 55 | if err != nil { 56 | return nil, fmt.Errorf("could not read data from the file %s", baselinePath) 57 | } 58 | 59 | err = json.Unmarshal(bytes, &previousFindings) 60 | if err != nil { 61 | return nil, fmt.Errorf("the format of the file %s is not supported", baselinePath) 62 | } 63 | 64 | return previousFindings, nil 65 | } 66 | -------------------------------------------------------------------------------- /report/json_test.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | func TestWriteJSON(t *testing.T) { 12 | tests := []struct { 13 | findings []Finding 14 | testReportName string 15 | expected string 16 | wantEmpty bool 17 | }{ 18 | { 19 | testReportName: "simple", 20 | expected: filepath.Join(expectPath, "report", "json_simple.json"), 21 | findings: []Finding{ 22 | { 23 | 24 | Description: "", 25 | RuleID: "test-rule", 26 | Match: "line containing secret", 27 | Secret: "a secret", 28 | StartLine: 1, 29 | EndLine: 2, 30 | StartColumn: 1, 31 | EndColumn: 2, 32 | Message: "opps", 33 | File: "auth.py", 34 | SymlinkFile: "", 35 | Commit: "0000000000000000", 36 | Author: "John Doe", 37 | Email: "johndoe@gmail.com", 38 | Date: "10-19-2003", 39 | Tags: []string{}, 40 | }, 41 | }}, 42 | { 43 | 44 | testReportName: "empty", 45 | expected: filepath.Join(expectPath, "report", "empty.json"), 46 | findings: []Finding{}}, 47 | } 48 | 49 | for _, test := range tests { 50 | tmpfile, err := os.Create(filepath.Join(t.TempDir(), test.testReportName+".json")) 51 | if err != nil { 52 | t.Error(err) 53 | } 54 | err = writeJson(test.findings, tmpfile) 55 | if err != nil { 56 | t.Error(err) 57 | } 58 | got, err := os.ReadFile(tmpfile.Name()) 59 | if err != nil { 60 | t.Error(err) 61 | } 62 | if test.wantEmpty { 63 | if len(got) > 0 { 64 | t.Errorf("Expected empty file, got %s", got) 65 | } 66 | continue 67 | } 68 | want, err := os.ReadFile(test.expected) 69 | if err != nil { 70 | t.Error(err) 71 | } 72 | 73 | if !bytes.Equal(got, want) { 74 | err = os.WriteFile(strings.Replace(test.expected, ".json", ".got.json", 1), got, 0644) 75 | if err != nil { 76 | t.Error(err) 77 | } 78 | t.Errorf("got %s, want %s", string(got), string(want)) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /report/csv_test.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | func TestWriteCSV(t *testing.T) { 12 | tests := []struct { 13 | findings []Finding 14 | testReportName string 15 | expected string 16 | wantEmpty bool 17 | }{ 18 | { 19 | testReportName: "simple", 20 | expected: filepath.Join(expectPath, "report", "csv_simple.csv"), 21 | findings: []Finding{ 22 | { 23 | RuleID: "test-rule", 24 | Match: "line containing secret", 25 | Secret: "a secret", 26 | StartLine: 1, 27 | EndLine: 2, 28 | StartColumn: 1, 29 | EndColumn: 2, 30 | Message: "opps", 31 | File: "auth.py", 32 | SymlinkFile: "", 33 | Commit: "0000000000000000", 34 | Author: "John Doe", 35 | Email: "johndoe@gmail.com", 36 | Date: "10-19-2003", 37 | Fingerprint: "fingerprint", 38 | Tags: []string{"tag1", "tag2", "tag3"}, 39 | }, 40 | }}, 41 | { 42 | 43 | wantEmpty: true, 44 | testReportName: "empty", 45 | expected: filepath.Join(expectPath, "report", "this_should_not_exist.csv"), 46 | findings: []Finding{}}, 47 | } 48 | 49 | for _, test := range tests { 50 | tmpfile, err := os.Create(filepath.Join(t.TempDir(), test.testReportName+".csv")) 51 | if err != nil { 52 | t.Error(err) 53 | } 54 | err = writeCsv(test.findings, tmpfile) 55 | if err != nil { 56 | t.Error(err) 57 | } 58 | got, err := os.ReadFile(tmpfile.Name()) 59 | if err != nil { 60 | t.Error(err) 61 | } 62 | if test.wantEmpty { 63 | if len(got) > 0 { 64 | t.Errorf("Expected empty file, got %s", got) 65 | } 66 | continue 67 | } 68 | want, err := os.ReadFile(test.expected) 69 | if err != nil { 70 | t.Error(err) 71 | } 72 | 73 | if !bytes.Equal(got, want) { 74 | err = os.WriteFile(strings.Replace(test.expected, ".csv", ".got.csv", 1), got, 0644) 75 | if err != nil { 76 | t.Error(err) 77 | } 78 | t.Errorf("got %s, want %s", string(got), string(want)) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /config/allowlist_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCommitAllowed(t *testing.T) { 11 | tests := []struct { 12 | allowlist Allowlist 13 | commit string 14 | commitAllowed bool 15 | }{ 16 | { 17 | allowlist: Allowlist{ 18 | Commits: []string{"commitA"}, 19 | }, 20 | commit: "commitA", 21 | commitAllowed: true, 22 | }, 23 | { 24 | allowlist: Allowlist{ 25 | Commits: []string{"commitB"}, 26 | }, 27 | commit: "commitA", 28 | commitAllowed: false, 29 | }, 30 | { 31 | allowlist: Allowlist{ 32 | Commits: []string{"commitB"}, 33 | }, 34 | commit: "", 35 | commitAllowed: false, 36 | }, 37 | } 38 | for _, tt := range tests { 39 | assert.Equal(t, tt.commitAllowed, tt.allowlist.CommitAllowed(tt.commit)) 40 | } 41 | } 42 | 43 | func TestRegexAllowed(t *testing.T) { 44 | tests := []struct { 45 | allowlist Allowlist 46 | secret string 47 | regexAllowed bool 48 | }{ 49 | { 50 | allowlist: Allowlist{ 51 | Regexes: []*regexp.Regexp{regexp.MustCompile("matchthis")}, 52 | }, 53 | secret: "a secret: matchthis, done", 54 | regexAllowed: true, 55 | }, 56 | { 57 | allowlist: Allowlist{ 58 | Regexes: []*regexp.Regexp{regexp.MustCompile("matchthis")}, 59 | }, 60 | secret: "a secret", 61 | regexAllowed: false, 62 | }, 63 | } 64 | for _, tt := range tests { 65 | assert.Equal(t, tt.regexAllowed, tt.allowlist.RegexAllowed(tt.secret)) 66 | } 67 | } 68 | 69 | func TestPathAllowed(t *testing.T) { 70 | tests := []struct { 71 | allowlist Allowlist 72 | path string 73 | pathAllowed bool 74 | }{ 75 | { 76 | allowlist: Allowlist{ 77 | Paths: []*regexp.Regexp{regexp.MustCompile("path")}, 78 | }, 79 | path: "a path", 80 | pathAllowed: true, 81 | }, 82 | { 83 | allowlist: Allowlist{ 84 | Paths: []*regexp.Regexp{regexp.MustCompile("path")}, 85 | }, 86 | path: "a ???", 87 | pathAllowed: false, 88 | }, 89 | } 90 | for _, tt := range tests { 91 | assert.Equal(t, tt.pathAllowed, tt.allowlist.PathAllowed(tt.path)) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /testdata/expected/report/junit_simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | { "Description": "Test Rule", "StartLine": 1, "EndLine": 2, "StartColumn": 1, "EndColumn": 2, "Match": "line containing secret", "Secret": "a secret", "File": "auth.py", "SymlinkFile": "", "Commit": "0000000000000000", "Entropy": 0, "Author": "John Doe", "Email": "johndoe@gmail.com", "Date": "10-19-2003", "Message": "opps", "Tags": [], "RuleID": "test-rule", "Fingerprint": "" } 6 | 7 | 8 | { "Description": "Test Rule", "StartLine": 2, "EndLine": 3, "StartColumn": 1, "EndColumn": 2, "Match": "line containing secret", "Secret": "a secret", "File": "auth.py", "SymlinkFile": "", "Commit": "", "Entropy": 0, "Author": "", "Email": "", "Date": "", "Message": "", "Tags": [], "RuleID": "test-rule", "Fingerprint": "" } 9 | 10 | 11 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/config.tmpl: -------------------------------------------------------------------------------- 1 | # This file has been auto-generated. Do not edit manually. 2 | # If you would like to contribute new rules, please use 3 | # cmd/generate/config/main.go and follow the contributing guidelines 4 | # at https://github.com/zricethezav/gitleaks/blob/master/CONTRIBUTING.md 5 | 6 | # This is the default gitleaks configuration file. 7 | # Rules and allowlists are defined within this file. 8 | # Rules instruct gitleaks on what should be considered a secret. 9 | # Allowlists instruct gitleaks on what is allowed, i.e. not a secret. 10 | 11 | title = "gitleaks config" 12 | 13 | [allowlist] 14 | description = "global allow lists" 15 | paths = [ 16 | '''gitleaks.toml''', 17 | '''(.*?)(jpg|gif|doc|docx|zip|xls|pdf|bin|svg|socket|vsidx|v2|suo|wsuo|.dll|pdb|exe)$''', 18 | '''(go.mod|go.sum)$''', 19 | '''gradle.lockfile''', 20 | '''node_modules''', 21 | '''package-lock.json''', 22 | '''pnpm-lock.yaml''', 23 | '''Database.refactorlog''', 24 | '''vendor''', 25 | ] 26 | 27 | {{ range $i, $rule := .Rules }}[[rules]] 28 | {{ if and $rule.SecretGroup $rule.Entropy $rule.Allowlist.StopWords }}description = "{{$rule.Description}}" 29 | id = "{{$rule.RuleID}}" 30 | regex = '''{{$rule.Regex}}''' 31 | secretGroup = {{ $rule.SecretGroup }} 32 | entropy = {{ $rule.Entropy}} 33 | keywords = [ 34 | {{ range $j, $keyword := $rule.Keywords }}"{{$keyword}}",{{end}} 35 | ] 36 | [rules.allowlist] 37 | stopwords= [{{ range $j, $stopword := $rule.Allowlist.StopWords }} 38 | "{{$stopword}}",{{end}} 39 | ] 40 | {{ else if and $rule.SecretGroup $rule.Entropy }}description = "{{$rule.Description}}" 41 | id = "{{$rule.RuleID}}" 42 | regex = '''{{$rule.Regex}}''' 43 | secretGroup = {{ $rule.SecretGroup }} 44 | entropy = {{ $rule.Entropy}} 45 | keywords = [ 46 | {{ range $j, $keyword := $rule.Keywords }}"{{$keyword}}",{{end}} 47 | ] 48 | 49 | {{ else if $rule.SecretGroup }}description = "{{$rule.Description}}" 50 | id = "{{$rule.RuleID}}" 51 | regex = '''{{$rule.Regex}}''' 52 | secretGroup = {{ $rule.SecretGroup }} 53 | keywords = [ 54 | {{ range $j, $keyword := $rule.Keywords }}"{{$keyword}}",{{end}} 55 | ] 56 | 57 | {{ else }}description = "{{$rule.Description}}" 58 | id = "{{$rule.RuleID}}" 59 | regex = '''{{$rule.Regex}}''' 60 | keywords = [ 61 | {{ range $j, $keyword := $rule.Keywords }}"{{$keyword}}",{{end}} 62 | ] 63 | 64 | {{end}}{{end}} 65 | -------------------------------------------------------------------------------- /testdata/repos/staged/dotGit/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git 2 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896362 -0500 checkout: moving from main to remove-secrets 3 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 906335481df9a4b48906c90318b4fac76b67fe73 Zach Rice 1635896426 -0500 commit: load token via env var 4 | 906335481df9a4b48906c90318b4fac76b67fe73 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896518 -0500 commit: add api package 5 | a122b33c6bad3ee54724f52f2caad385ab1982ab a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896543 -0500 checkout: moving from remove-secrets to api-pkg 6 | a122b33c6bad3ee54724f52f2caad385ab1982ab 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896644 -0500 checkout: moving from api-pkg to main 7 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: Fast-forward 8 | 2e1db472eeba53f06c4026ae4566ea022e36598e 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896716 -0500 checkout: moving from main to foo 9 | 2e1db472eeba53f06c4026ae4566ea022e36598e 491504d5a31946ce75e22554cc34203d8e5ff3ca Zach Rice 1635896886 -0500 commit: adding foo package with secret 10 | 491504d5a31946ce75e22554cc34203d8e5ff3ca f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896931 -0500 commit: removing secret from foo package 11 | f1b58b97808f8e744f6a23c693859df5b5968901 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635897009 -0500 checkout: moving from foo to main 12 | 2e1db472eeba53f06c4026ae4566ea022e36598e f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635897062 -0500 checkout: moving from main to foo 13 | f1b58b97808f8e744f6a23c693859df5b5968901 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635897508 -0500 checkout: moving from foo to main 14 | 2e1db472eeba53f06c4026ae4566ea022e36598e bf3f24164d7256b4021575cbdb2f97b98e6f057e Rafael Figueiredo 1679239434 -0300 commit: add .gitleaksignore file 15 | -------------------------------------------------------------------------------- /detect/location.go: -------------------------------------------------------------------------------- 1 | package detect 2 | 3 | // Location represents a location in a file 4 | type Location struct { 5 | startLine int 6 | endLine int 7 | startColumn int 8 | endColumn int 9 | startLineIndex int 10 | endLineIndex int 11 | } 12 | 13 | func location(fragment Fragment, matchIndex []int) Location { 14 | var ( 15 | prevNewLine int 16 | location Location 17 | lineSet bool 18 | _lineNum int 19 | ) 20 | 21 | start := matchIndex[0] 22 | end := matchIndex[1] 23 | 24 | // default startLineIndex to 0 25 | location.startLineIndex = 0 26 | 27 | // Fixes: https://github.com/zricethezav/gitleaks/issues/1037 28 | // When a fragment does NOT have any newlines, a default "newline" 29 | // will be counted to make the subsequent location calculation logic work 30 | // for fragments will no newlines. 31 | if len(fragment.newlineIndices) == 0 { 32 | fragment.newlineIndices = [][]int{ 33 | {len(fragment.Raw), len(fragment.Raw) + 1}, 34 | } 35 | } 36 | 37 | for lineNum, pair := range fragment.newlineIndices { 38 | _lineNum = lineNum 39 | newLineByteIndex := pair[0] 40 | if prevNewLine <= start && start < newLineByteIndex { 41 | lineSet = true 42 | location.startLine = lineNum 43 | location.endLine = lineNum 44 | location.startColumn = (start - prevNewLine) + 1 // +1 because counting starts at 1 45 | location.startLineIndex = prevNewLine 46 | location.endLineIndex = newLineByteIndex 47 | } 48 | if prevNewLine < end && end <= newLineByteIndex { 49 | location.endLine = lineNum 50 | location.endColumn = (end - prevNewLine) 51 | location.endLineIndex = newLineByteIndex 52 | } 53 | prevNewLine = pair[0] 54 | } 55 | 56 | if !lineSet { 57 | // if lines never get set then that means the secret is most likely 58 | // on the last line of the diff output and the diff output does not have 59 | // a newline 60 | location.startColumn = (start - prevNewLine) + 1 // +1 because counting starts at 1 61 | location.endColumn = (end - prevNewLine) 62 | location.startLine = _lineNum + 1 63 | location.endLine = _lineNum + 1 64 | 65 | // search for new line byte index 66 | i := 0 67 | for end+i < len(fragment.Raw) { 68 | if fragment.Raw[end+i] == '\n' { 69 | break 70 | } 71 | if fragment.Raw[end+i] == '\r' { 72 | break 73 | } 74 | i++ 75 | } 76 | location.endLineIndex = end + i 77 | } 78 | return location 79 | } 80 | -------------------------------------------------------------------------------- /report/report_test.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "strconv" 7 | "testing" 8 | 9 | "github.com/zricethezav/gitleaks/v8/config" 10 | ) 11 | 12 | const ( 13 | expectPath = "../testdata/expected/" 14 | ) 15 | 16 | func TestReport(t *testing.T) { 17 | tests := []struct { 18 | findings []Finding 19 | ext string 20 | wantEmpty bool 21 | }{ 22 | { 23 | ext: "json", 24 | findings: []Finding{ 25 | { 26 | RuleID: "test-rule", 27 | }, 28 | }, 29 | }, 30 | { 31 | ext: ".json", 32 | findings: []Finding{ 33 | { 34 | RuleID: "test-rule", 35 | }, 36 | }, 37 | }, 38 | { 39 | ext: ".jsonj", 40 | findings: []Finding{ 41 | { 42 | RuleID: "test-rule", 43 | }, 44 | }, 45 | wantEmpty: true, 46 | }, 47 | { 48 | ext: ".csv", 49 | findings: []Finding{ 50 | { 51 | RuleID: "test-rule", 52 | }, 53 | }, 54 | }, 55 | { 56 | ext: "csv", 57 | findings: []Finding{ 58 | { 59 | RuleID: "test-rule", 60 | }, 61 | }, 62 | }, 63 | { 64 | ext: "CSV", 65 | findings: []Finding{ 66 | { 67 | RuleID: "test-rule", 68 | }, 69 | }, 70 | }, 71 | { 72 | ext: ".xml", 73 | findings: []Finding{ 74 | { 75 | RuleID: "test-rule", 76 | }, 77 | }, 78 | }, 79 | { 80 | ext: "junit", 81 | findings: []Finding{ 82 | { 83 | RuleID: "test-rule", 84 | }, 85 | }, 86 | }, 87 | // { 88 | // ext: "SARIF", 89 | // findings: []Finding{ 90 | // { 91 | // RuleID: "test-rule", 92 | // }, 93 | // }, 94 | // }, 95 | } 96 | 97 | for i, test := range tests { 98 | tmpfile, err := os.Create(filepath.Join(t.TempDir(), strconv.Itoa(i)+test.ext)) 99 | if err != nil { 100 | t.Error(err) 101 | } 102 | err = Write(test.findings, config.Config{}, test.ext, tmpfile.Name()) 103 | if err != nil { 104 | t.Error(err) 105 | } 106 | got, err := os.ReadFile(tmpfile.Name()) 107 | if err != nil { 108 | t.Error(err) 109 | } 110 | 111 | if len(got) == 0 && !test.wantEmpty { 112 | t.Errorf("got empty file with extension " + test.ext) 113 | } 114 | 115 | if test.wantEmpty { 116 | if len(got) > 0 { 117 | t.Errorf("Expected empty file, got %s", got) 118 | } 119 | continue 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/planetscale.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func PlanetScalePassword() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | RuleID: "planetscale-password", 12 | Description: "PlanetScale password", 13 | Regex: generateUniqueTokenRegex(`pscale_pw_(?i)[a-z0-9=\-_\.]{32,64}`), 14 | SecretGroup: 1, 15 | Keywords: []string{ 16 | "pscale_pw_", 17 | }, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | generateSampleSecret("planetScalePassword", "pscale_pw_"+secrets.NewSecret(alphaNumericExtended("32"))), 23 | generateSampleSecret("planetScalePassword", "pscale_pw_"+secrets.NewSecret(alphaNumericExtended("43"))), 24 | generateSampleSecret("planetScalePassword", "pscale_pw_"+secrets.NewSecret(alphaNumericExtended("64"))), 25 | } 26 | return validate(r, tps, nil) 27 | } 28 | 29 | func PlanetScaleAPIToken() *config.Rule { 30 | // define rule 31 | r := config.Rule{ 32 | RuleID: "planetscale-api-token", 33 | Description: "PlanetScale API token", 34 | Regex: generateUniqueTokenRegex(`pscale_tkn_(?i)[a-z0-9=\-_\.]{32,64}`), 35 | SecretGroup: 1, 36 | Keywords: []string{ 37 | "pscale_tkn_", 38 | }, 39 | } 40 | 41 | // validate 42 | tps := []string{ 43 | generateSampleSecret("planetScalePassword", "pscale_tkn_"+secrets.NewSecret(alphaNumericExtended("32"))), 44 | generateSampleSecret("planetScalePassword", "pscale_tkn_"+secrets.NewSecret(alphaNumericExtended("43"))), 45 | generateSampleSecret("planetScalePassword", "pscale_tkn_"+secrets.NewSecret(alphaNumericExtended("64"))), 46 | } 47 | return validate(r, tps, nil) 48 | } 49 | 50 | func PlanetScaleOAuthToken() *config.Rule { 51 | // define rule 52 | r := config.Rule{ 53 | RuleID: "planetscale-oauth-token", 54 | Description: "PlanetScale OAuth token", 55 | Regex: generateUniqueTokenRegex(`pscale_oauth_(?i)[a-z0-9=\-_\.]{32,64}`), 56 | SecretGroup: 1, 57 | Keywords: []string{ 58 | "pscale_oauth_", 59 | }, 60 | } 61 | 62 | // validate 63 | tps := []string{ 64 | generateSampleSecret("planetScalePassword", "pscale_oauth_"+secrets.NewSecret(alphaNumericExtended("32"))), 65 | generateSampleSecret("planetScalePassword", "pscale_oauth_"+secrets.NewSecret(alphaNumericExtended("43"))), 66 | generateSampleSecret("planetScalePassword", "pscale_oauth_"+secrets.NewSecret(alphaNumericExtended("64"))), 67 | } 68 | return validate(r, tps, nil) 69 | } 70 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/sidekiq.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/config" 7 | ) 8 | 9 | func SidekiqSecret() *config.Rule { 10 | // define rule 11 | r := config.Rule{ 12 | Description: "Sidekiq Secret", 13 | RuleID: "sidekiq-secret", 14 | SecretGroup: 1, 15 | Regex: generateSemiGenericRegex([]string{"BUNDLE_ENTERPRISE__CONTRIBSYS__COM", "BUNDLE_GEMS__CONTRIBSYS__COM"}, 16 | `[a-f0-9]{8}:[a-f0-9]{8}`), 17 | Keywords: []string{"BUNDLE_ENTERPRISE__CONTRIBSYS__COM", "BUNDLE_GEMS__CONTRIBSYS__COM"}, 18 | } 19 | 20 | // validate 21 | tps := []string{ 22 | "BUNDLE_ENTERPRISE__CONTRIBSYS__COM: cafebabe:deadbeef", 23 | "export BUNDLE_ENTERPRISE__CONTRIBSYS__COM=cafebabe:deadbeef", 24 | "export BUNDLE_ENTERPRISE__CONTRIBSYS__COM = cafebabe:deadbeef", 25 | "BUNDLE_GEMS__CONTRIBSYS__COM: \"cafebabe:deadbeef\"", 26 | "export BUNDLE_GEMS__CONTRIBSYS__COM=\"cafebabe:deadbeef\"", 27 | "export BUNDLE_GEMS__CONTRIBSYS__COM = \"cafebabe:deadbeef\"", 28 | "export BUNDLE_ENTERPRISE__CONTRIBSYS__COM=cafebabe:deadbeef;", 29 | "export BUNDLE_ENTERPRISE__CONTRIBSYS__COM=cafebabe:deadbeef && echo 'hello world'", 30 | } 31 | return validate(r, tps, nil) 32 | } 33 | 34 | func SidekiqSensitiveUrl() *config.Rule { 35 | // define rule 36 | r := config.Rule{ 37 | Description: "Sidekiq Sensitive URL", 38 | RuleID: "sidekiq-sensitive-url", 39 | SecretGroup: 2, 40 | Regex: regexp.MustCompile(`(?i)\b(http(?:s??):\/\/)([a-f0-9]{8}:[a-f0-9]{8})@(?:gems.contribsys.com|enterprise.contribsys.com)(?:[\/|\#|\?|:]|$)`), 41 | Keywords: []string{"gems.contribsys.com", "enterprise.contribsys.com"}, 42 | } 43 | 44 | // validate 45 | tps := []string{ 46 | "https://cafebabe:deadbeef@gems.contribsys.com/", 47 | "https://cafebabe:deadbeef@gems.contribsys.com", 48 | "https://cafeb4b3:d3adb33f@enterprise.contribsys.com/", 49 | "https://cafeb4b3:d3adb33f@enterprise.contribsys.com", 50 | "http://cafebabe:deadbeef@gems.contribsys.com/", 51 | "http://cafebabe:deadbeef@gems.contribsys.com", 52 | "http://cafeb4b3:d3adb33f@enterprise.contribsys.com/", 53 | "http://cafeb4b3:d3adb33f@enterprise.contribsys.com", 54 | "http://cafeb4b3:d3adb33f@enterprise.contribsys.com#heading1", 55 | "http://cafeb4b3:d3adb33f@enterprise.contribsys.com?param1=true¶m2=false", 56 | "http://cafeb4b3:d3adb33f@enterprise.contribsys.com:80", 57 | "http://cafeb4b3:d3adb33f@enterprise.contribsys.com:80/path?param1=true¶m2=false#heading1", 58 | } 59 | return validate(r, tps, nil) 60 | } 61 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/github.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 7 | "github.com/zricethezav/gitleaks/v8/config" 8 | ) 9 | 10 | func GitHubPat() *config.Rule { 11 | // define rule 12 | r := config.Rule{ 13 | Description: "GitHub Personal Access Token", 14 | RuleID: "github-pat", 15 | Regex: regexp.MustCompile(`ghp_[0-9a-zA-Z]{36}`), 16 | Keywords: []string{"ghp_"}, 17 | } 18 | 19 | // validate 20 | tps := []string{ 21 | generateSampleSecret("github", "ghp_"+secrets.NewSecret(alphaNumeric("36"))), 22 | } 23 | return validate(r, tps, nil) 24 | } 25 | 26 | func GitHubFineGrainedPat() *config.Rule { 27 | // define rule 28 | r := config.Rule{ 29 | Description: "GitHub Fine-Grained Personal Access Token", 30 | RuleID: "github-fine-grained-pat", 31 | Regex: regexp.MustCompile(`github_pat_[0-9a-zA-Z_]{82}`), 32 | Keywords: []string{"github_pat_"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("github", "github_pat_"+secrets.NewSecret(alphaNumeric("82"))), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | 42 | func GitHubOauth() *config.Rule { 43 | // define rule 44 | r := config.Rule{ 45 | Description: "GitHub OAuth Access Token", 46 | RuleID: "github-oauth", 47 | Regex: regexp.MustCompile(`gho_[0-9a-zA-Z]{36}`), 48 | Keywords: []string{"gho_"}, 49 | } 50 | 51 | // validate 52 | tps := []string{ 53 | generateSampleSecret("github", "gho_"+secrets.NewSecret(alphaNumeric("36"))), 54 | } 55 | return validate(r, tps, nil) 56 | } 57 | 58 | func GitHubApp() *config.Rule { 59 | // define rule 60 | r := config.Rule{ 61 | Description: "GitHub App Token", 62 | RuleID: "github-app-token", 63 | Regex: regexp.MustCompile(`(ghu|ghs)_[0-9a-zA-Z]{36}`), 64 | Keywords: []string{"ghu_", "ghs_"}, 65 | } 66 | 67 | // validate 68 | tps := []string{ 69 | generateSampleSecret("github", "ghu_"+secrets.NewSecret(alphaNumeric("36"))), 70 | generateSampleSecret("github", "ghs_"+secrets.NewSecret(alphaNumeric("36"))), 71 | } 72 | return validate(r, tps, nil) 73 | } 74 | 75 | func GitHubRefresh() *config.Rule { 76 | // define rule 77 | r := config.Rule{ 78 | Description: "GitHub Refresh Token", 79 | RuleID: "github-refresh-token", 80 | Regex: regexp.MustCompile(`ghr_[0-9a-zA-Z]{36}`), 81 | Keywords: []string{"ghr_"}, 82 | } 83 | 84 | // validate 85 | tps := []string{ 86 | generateSampleSecret("github", "ghr_"+secrets.NewSecret(alphaNumeric("36"))), 87 | } 88 | return validate(r, tps, nil) 89 | } 90 | -------------------------------------------------------------------------------- /report/sarif_test.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/spf13/viper" 12 | "github.com/zricethezav/gitleaks/v8/config" 13 | ) 14 | 15 | const configPath = "../testdata/config/" 16 | 17 | func TestWriteSarif(t *testing.T) { 18 | tests := []struct { 19 | findings []Finding 20 | testReportName string 21 | expected string 22 | wantEmpty bool 23 | cfgName string 24 | }{ 25 | { 26 | cfgName: "simple", 27 | testReportName: "simple", 28 | expected: filepath.Join(expectPath, "report", "sarif_simple.sarif"), 29 | findings: []Finding{ 30 | { 31 | 32 | Description: "A test rule", 33 | RuleID: "test-rule", 34 | Match: "line containing secret", 35 | Secret: "a secret", 36 | StartLine: 1, 37 | EndLine: 2, 38 | StartColumn: 1, 39 | EndColumn: 2, 40 | Message: "opps", 41 | File: "auth.py", 42 | Commit: "0000000000000000", 43 | Author: "John Doe", 44 | Email: "johndoe@gmail.com", 45 | Date: "10-19-2003", 46 | Tags: []string{"tag1", "tag2", "tag3"}, 47 | }, 48 | }}, 49 | } 50 | 51 | for _, test := range tests { 52 | tmpfile, err := os.Create(filepath.Join(t.TempDir(), test.testReportName+".json")) 53 | if err != nil { 54 | t.Error(err) 55 | } 56 | viper.Reset() 57 | viper.AddConfigPath(configPath) 58 | viper.SetConfigName(test.cfgName) 59 | viper.SetConfigType("toml") 60 | err = viper.ReadInConfig() 61 | if err != nil { 62 | t.Error(err) 63 | } 64 | 65 | var vc config.ViperConfig 66 | err = viper.Unmarshal(&vc) 67 | if err != nil { 68 | t.Error(err) 69 | } 70 | 71 | cfg, err := vc.Translate() 72 | if err != nil { 73 | t.Error(err) 74 | } 75 | err = writeSarif(cfg, test.findings, tmpfile) 76 | fmt.Println(cfg) 77 | if err != nil { 78 | t.Error(err) 79 | } 80 | got, err := os.ReadFile(tmpfile.Name()) 81 | if err != nil { 82 | t.Error(err) 83 | } 84 | if test.wantEmpty { 85 | if len(got) > 0 { 86 | t.Errorf("Expected empty file, got %s", got) 87 | } 88 | continue 89 | } 90 | want, err := os.ReadFile(test.expected) 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | 95 | if !bytes.Equal(got, want) { 96 | err = os.WriteFile(strings.Replace(test.expected, ".sarif", ".got.sarif", 1), got, 0644) 97 | if err != nil { 98 | t.Error(err) 99 | } 100 | t.Errorf("got %s, want %s", string(got), string(want)) 101 | } 102 | 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /cmd/generate/config/rules/twitter.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets" 5 | "github.com/zricethezav/gitleaks/v8/config" 6 | ) 7 | 8 | func TwitterAPIKey() *config.Rule { 9 | // define rule 10 | r := config.Rule{ 11 | Description: "Twitter API Key", 12 | RuleID: "twitter-api-key", 13 | Regex: generateSemiGenericRegex([]string{"twitter"}, alphaNumeric("25")), 14 | SecretGroup: 1, 15 | Keywords: []string{"twitter"}, 16 | } 17 | 18 | // validate 19 | tps := []string{ 20 | generateSampleSecret("twitter", secrets.NewSecret(alphaNumeric("25"))), 21 | } 22 | return validate(r, tps, nil) 23 | } 24 | 25 | func TwitterAPISecret() *config.Rule { 26 | // define rule 27 | r := config.Rule{ 28 | Description: "Twitter API Secret", 29 | RuleID: "twitter-api-secret", 30 | Regex: generateSemiGenericRegex([]string{"twitter"}, alphaNumeric("50")), 31 | SecretGroup: 1, 32 | Keywords: []string{"twitter"}, 33 | } 34 | 35 | // validate 36 | tps := []string{ 37 | generateSampleSecret("twitter", secrets.NewSecret(alphaNumeric("50"))), 38 | } 39 | return validate(r, tps, nil) 40 | } 41 | 42 | func TwitterBearerToken() *config.Rule { 43 | // define rule 44 | r := config.Rule{ 45 | Description: "Twitter Bearer Token", 46 | RuleID: "twitter-bearer-token", 47 | Regex: generateSemiGenericRegex([]string{"twitter"}, "A{22}[a-zA-Z0-9%]{80,100}"), 48 | SecretGroup: 1, 49 | Keywords: []string{"twitter"}, 50 | } 51 | 52 | // validate 53 | tps := []string{ 54 | generateSampleSecret("twitter", secrets.NewSecret("A{22}[a-zA-Z0-9%]{80,100}")), 55 | } 56 | return validate(r, tps, nil) 57 | } 58 | 59 | func TwitterAccessToken() *config.Rule { 60 | // define rule 61 | r := config.Rule{ 62 | Description: "Twitter Access Token", 63 | RuleID: "twitter-access-token", 64 | Regex: generateSemiGenericRegex([]string{"twitter"}, "[0-9]{15,25}-[a-zA-Z0-9]{20,40}"), 65 | SecretGroup: 1, 66 | Keywords: []string{"twitter"}, 67 | } 68 | 69 | // validate 70 | tps := []string{ 71 | generateSampleSecret("twitter", secrets.NewSecret("[0-9]{15,25}-[a-zA-Z0-9]{20,40}")), 72 | } 73 | return validate(r, tps, nil) 74 | } 75 | 76 | func TwitterAccessSecret() *config.Rule { 77 | // define rule 78 | r := config.Rule{ 79 | Description: "Twitter Access Secret", 80 | RuleID: "twitter-access-secret", 81 | Regex: generateSemiGenericRegex([]string{"twitter"}, alphaNumeric("45")), 82 | SecretGroup: 1, 83 | Keywords: []string{"twitter"}, 84 | } 85 | 86 | // validate 87 | tps := []string{ 88 | generateSampleSecret("twitter", secrets.NewSecret(alphaNumeric("45"))), 89 | } 90 | return validate(r, tps, nil) 91 | } 92 | -------------------------------------------------------------------------------- /report/junit.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "encoding/json" 5 | "encoding/xml" 6 | "fmt" 7 | "io" 8 | "strconv" 9 | ) 10 | 11 | func writeJunit(findings []Finding, w io.WriteCloser) error { 12 | testSuites := TestSuites{ 13 | TestSuites: getTestSuites(findings), 14 | } 15 | 16 | io.WriteString(w, xml.Header) 17 | encoder := xml.NewEncoder(w) 18 | encoder.Indent("", "\t") 19 | return encoder.Encode(testSuites) 20 | } 21 | 22 | func getTestSuites(findings []Finding) []TestSuite { 23 | return []TestSuite{ 24 | { 25 | Failures: strconv.Itoa(len(findings)), 26 | Name: "gitleaks", 27 | Tests: strconv.Itoa(len(findings)), 28 | TestCases: getTestCases(findings), 29 | Time: "", 30 | }, 31 | } 32 | } 33 | 34 | func getTestCases(findings []Finding) []TestCase { 35 | testCases := []TestCase{} 36 | for _, f := range findings { 37 | testCase := TestCase{ 38 | Classname: f.Description, 39 | Failure: getFailure(f), 40 | File: f.File, 41 | Name: getMessage(f), 42 | Time: "", 43 | } 44 | testCases = append(testCases, testCase) 45 | } 46 | return testCases 47 | } 48 | 49 | func getFailure(f Finding) Failure { 50 | return Failure{ 51 | Data: getData(f), 52 | Message: getMessage(f), 53 | Type: f.Description, 54 | } 55 | } 56 | 57 | func getData(f Finding) string { 58 | data, err := json.MarshalIndent(f, "", "\t") 59 | if err != nil { 60 | fmt.Println(err) 61 | return "" 62 | } 63 | return string(data) 64 | } 65 | 66 | func getMessage(f Finding) string { 67 | if f.Commit == "" { 68 | return fmt.Sprintf("%s has detected a secret in file %s, line %s.", f.RuleID, f.File, strconv.Itoa(f.StartLine)) 69 | } 70 | 71 | return fmt.Sprintf("%s has detected a secret in file %s, line %s, at commit %s.", f.RuleID, f.File, strconv.Itoa(f.StartLine), f.Commit) 72 | } 73 | 74 | type TestSuites struct { 75 | XMLName xml.Name `xml:"testsuites"` 76 | TestSuites []TestSuite 77 | } 78 | 79 | type TestSuite struct { 80 | XMLName xml.Name `xml:"testsuite"` 81 | Failures string `xml:"failures,attr"` 82 | Name string `xml:"name,attr"` 83 | Tests string `xml:"tests,attr"` 84 | TestCases []TestCase `xml:"testcase"` 85 | Time string `xml:"time,attr"` 86 | } 87 | 88 | type TestCase struct { 89 | XMLName xml.Name `xml:"testcase"` 90 | Classname string `xml:"classname,attr"` 91 | Failure Failure `xml:"failure"` 92 | File string `xml:"file,attr"` 93 | Name string `xml:"name,attr"` 94 | Time string `xml:"time,attr"` 95 | } 96 | 97 | type Failure struct { 98 | XMLName xml.Name `xml:"failure"` 99 | Data string `xml:",chardata"` 100 | Message string `xml:"message,attr"` 101 | Type string `xml:"type,attr"` 102 | } 103 | -------------------------------------------------------------------------------- /testdata/repos/small/dotGit/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git 2 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896362 -0500 checkout: moving from main to remove-secrets 3 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 906335481df9a4b48906c90318b4fac76b67fe73 Zach Rice 1635896426 -0500 commit: load token via env var 4 | 906335481df9a4b48906c90318b4fac76b67fe73 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896518 -0500 commit: add api package 5 | a122b33c6bad3ee54724f52f2caad385ab1982ab a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896543 -0500 checkout: moving from remove-secrets to api-pkg 6 | a122b33c6bad3ee54724f52f2caad385ab1982ab 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896644 -0500 checkout: moving from api-pkg to main 7 | 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: Fast-forward 8 | 2e1db472eeba53f06c4026ae4566ea022e36598e 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896716 -0500 checkout: moving from main to foo 9 | 2e1db472eeba53f06c4026ae4566ea022e36598e 491504d5a31946ce75e22554cc34203d8e5ff3ca Zach Rice 1635896886 -0500 commit: adding foo package with secret 10 | 491504d5a31946ce75e22554cc34203d8e5ff3ca f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896931 -0500 commit: removing secret from foo package 11 | f1b58b97808f8e744f6a23c693859df5b5968901 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635897009 -0500 checkout: moving from foo to main 12 | 2e1db472eeba53f06c4026ae4566ea022e36598e f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635897062 -0500 checkout: moving from main to foo 13 | f1b58b97808f8e744f6a23c693859df5b5968901 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635897508 -0500 checkout: moving from foo to main 14 | 2e1db472eeba53f06c4026ae4566ea022e36598e 4f77f1b3cc39d4b17e4cf4ba0a38f5daed9875b4 Richard Gomez 1681920523 -0400 commit: add .gitleaksignore test 15 | 4f77f1b3cc39d4b17e4cf4ba0a38f5daed9875b4 cada78a9bf157ec05573f19e682d211f811c2e2d Richard Gomez 1681920658 -0400 commit (amend): add .gitleaksignore test 16 | cada78a9bf157ec05573f19e682d211f811c2e2d 2e1db472eeba53f06c4026ae4566ea022e36598e Richard Gomez 1681920730 -0400 reset: moving to HEAD~ 17 | 2e1db472eeba53f06c4026ae4566ea022e36598e 53cd7a3c6eb4937f413e3c25e4a9f39289afa69e Richard Gomez 1681920759 -0400 commit: add .gitleaksignore test files 18 | -------------------------------------------------------------------------------- /report/junit_test.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestWriteJunit(t *testing.T) { 11 | tests := []struct { 12 | findings []Finding 13 | testReportName string 14 | expected string 15 | wantEmpty bool 16 | }{ 17 | { 18 | testReportName: "simple", 19 | expected: filepath.Join(expectPath, "report", "junit_simple.xml"), 20 | findings: []Finding{ 21 | { 22 | 23 | Description: "Test Rule", 24 | RuleID: "test-rule", 25 | Match: "line containing secret", 26 | Secret: "a secret", 27 | StartLine: 1, 28 | EndLine: 2, 29 | StartColumn: 1, 30 | EndColumn: 2, 31 | Message: "opps", 32 | File: "auth.py", 33 | Commit: "0000000000000000", 34 | Author: "John Doe", 35 | Email: "johndoe@gmail.com", 36 | Date: "10-19-2003", 37 | Tags: []string{}, 38 | }, 39 | { 40 | 41 | Description: "Test Rule", 42 | RuleID: "test-rule", 43 | Match: "line containing secret", 44 | Secret: "a secret", 45 | StartLine: 2, 46 | EndLine: 3, 47 | StartColumn: 1, 48 | EndColumn: 2, 49 | Message: "", 50 | File: "auth.py", 51 | Commit: "", 52 | Author: "", 53 | Email: "", 54 | Date: "", 55 | Tags: []string{}, 56 | }, 57 | }, 58 | }, 59 | { 60 | testReportName: "empty", 61 | expected: filepath.Join(expectPath, "report", "junit_empty.xml"), 62 | findings: []Finding{}, 63 | }, 64 | } 65 | 66 | for _, test := range tests { 67 | // create tmp file using os.TempDir() 68 | tmpfile, err := os.Create(filepath.Join(t.TempDir(), test.testReportName+".xml")) 69 | if err != nil { 70 | os.Remove(tmpfile.Name()) 71 | t.Error(err) 72 | } 73 | err = writeJunit(test.findings, tmpfile) 74 | if err != nil { 75 | os.Remove(tmpfile.Name()) 76 | t.Error(err) 77 | } 78 | got, err := os.ReadFile(tmpfile.Name()) 79 | if err != nil { 80 | os.Remove(tmpfile.Name()) 81 | t.Error(err) 82 | } 83 | if test.wantEmpty { 84 | if len(got) > 0 { 85 | os.Remove(tmpfile.Name()) 86 | t.Errorf("Expected empty file, got %s", got) 87 | } 88 | os.Remove(tmpfile.Name()) 89 | continue 90 | } 91 | want, err := os.ReadFile(test.expected) 92 | if err != nil { 93 | os.Remove(tmpfile.Name()) 94 | t.Error(err) 95 | } 96 | 97 | if string(got) != string(want) { 98 | err = os.WriteFile(strings.Replace(test.expected, ".xml", ".got.xml", 1), got, 0644) 99 | if err != nil { 100 | t.Error(err) 101 | } 102 | t.Errorf("got %s, want %s", string(got), string(want)) 103 | } 104 | 105 | os.Remove(tmpfile.Name()) 106 | } 107 | } 108 | --------------------------------------------------------------------------------