Compare commits

..

116 Commits

Author SHA1 Message Date
ahrav 477e2a1332 Update entrypoint (#1013)
* Update entrypoint.

* update comment.

* Call each extra arg on its own.

* Update loop.

* Update extra args.

* Use miccah's magic script.

* Fix path to bash.

* update entrypoint.

* Add bash to Dockerfile.

* update goreleaser dockerfile.
2023-01-11 18:58:05 -08:00
Bill Rich 430d5c764c Rename and export isGitSource (#1016) 2023-01-10 12:51:58 -08:00
Bill Rich 8b2e1d36cf Copy metadata for line number aware sources (#1011)
* Copy metadata for line number aware sources

* Improve style
2023-01-10 09:35:44 -08:00
Clark Brown 864cf00337 Revert "Allow for default value to be used. (#999)" (#1004)
This reverts commit ee6817ad85.
2023-01-09 18:09:58 -06:00
Miccah e5ede17c77 Validate custom regular expressions on detector initialization (#1010)
* Validate custom regular expressions on detector initialization

* Add regex name to error message
2023-01-09 17:30:47 -06:00
Miccah 74831f63d5 Capture callstack of canceled contexts (#979) 2023-01-09 17:27:06 -06:00
ahrav 09d4422cdb Handle invalid regex for custom detector. (#1005)
* Handle invalid regex for custom detector.

* Add comment highlighting invalid regex.
2023-01-09 09:45:30 -08:00
dependabot[bot] 7de0178842 Bump golang.org/x/crypto from 0.4.0 to 0.5.0 (#1009)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 09:32:29 -08:00
dependabot[bot] 6d384ce3e8 Bump github.com/hashicorp/go-retryablehttp from 0.7.1 to 0.7.2 (#1008)
Bumps [github.com/hashicorp/go-retryablehttp](https://github.com/hashicorp/go-retryablehttp) from 0.7.1 to 0.7.2.
- [Release notes](https://github.com/hashicorp/go-retryablehttp/releases)
- [Commits](https://github.com/hashicorp/go-retryablehttp/compare/v0.7.1...v0.7.2)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-retryablehttp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 09:31:23 -08:00
dependabot[bot] 0e24d406d5 Bump github.com/go-git/go-git/v5 from 5.5.1 to 5.5.2 (#1007)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.5.1 to 5.5.2.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.5.1...v5.5.2)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 09:30:56 -08:00
dependabot[bot] d72c31b8b6 Bump cloud.google.com/go/secretmanager from 1.9.0 to 1.10.0 (#1006)
Bumps [cloud.google.com/go/secretmanager](https://github.com/googleapis/google-cloud-go) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/googleapis/google-cloud-go/releases)
- [Changelog](https://github.com/googleapis/google-cloud-go/blob/main/documentai/CHANGES.md)
- [Commits](https://github.com/googleapis/google-cloud-go/compare/dlp/v1.9.0...asset/v1.10.0)

---
updated-dependencies:
- dependency-name: cloud.google.com/go/secretmanager
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 09:29:26 -08:00
Yassine Ilmi d720c0c0f3 Switch to retryableHttpClient for GitHub AuthN API Client + More Logs (#995)
* Adding missing flags to Readme

* Use retryableHttpClient by default for GitHub

* Adding repoUrl for scanning time log

* Use WithField instead of WithFields

* Updating README with lasted --help output
2023-01-09 09:21:56 -08:00
dependabot[bot] 705c01e5f3 Bump goreleaser/goreleaser-action from 3 to 4 (#980)
Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 3 to 4.
- [Release notes](https://github.com/goreleaser/goreleaser-action/releases)
- [Commits](https://github.com/goreleaser/goreleaser-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: goreleaser/goreleaser-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 09:20:41 -08:00
Pulkit Aggarwal fc6fd29f3f Fix GitUrl Return (#987)
Co-authored-by: ahrav <ahravdutta02@gmail.com>
2023-01-09 09:17:30 -08:00
ahrav ee6817ad85 Allow for default value to be used. (#999) 2023-01-06 16:05:02 -08:00
Dustin Decker 5f6143f09a Add Circle CI source (#997)
* Add Circle CI source

* remove SHA1 line

* remove trim
2023-01-05 21:44:37 -08:00
ahrav 3fadec950e Make GA action default base an empty string. (#996) 2023-01-05 16:48:07 -08:00
Yassine Ilmi 330a6f7cdc Removing Debug version Println to logrus debug (#993) 2023-01-03 10:36:27 -06:00
ahrav 009756dce6 add proto that was missing. (#986) 2022-12-23 13:27:07 -08:00
Miccah 8859771a2a Remove custom log leveler (#985)
Instead of manually using a log leveler, rely on the global one defined
in the `log` package.
2022-12-20 19:03:53 -06:00
Miccah 130d5ae3ad Add custom regex detector docs (#983) 2022-12-20 18:24:41 -06:00
Miccah f5b83ee2a5 Add configuration parsing and custom detectors to engine (#968)
* Add configuration parsing for custom detectors

* Error on empty filename
2022-12-20 10:14:49 -06:00
dependabot[bot] cc6bd31586 Bump golang.org/x/crypto from 0.3.0 to 0.4.0 (#982)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 15:45:21 -08:00
dependabot[bot] e3b6de0fdc Bump github.com/xanzy/go-gitlab from 0.76.0 to 0.77.0 (#981)
Bumps [github.com/xanzy/go-gitlab](https://github.com/xanzy/go-gitlab) from 0.76.0 to 0.77.0.
- [Release notes](https://github.com/xanzy/go-gitlab/releases)
- [Changelog](https://github.com/xanzy/go-gitlab/blob/master/releases_test.go)
- [Commits](https://github.com/xanzy/go-gitlab/compare/v0.76.0...v0.77.0)

---
updated-dependencies:
- dependency-name: github.com/xanzy/go-gitlab
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 15:32:16 -08:00
ahrav 936a139596 Allow using a glob for include list. (#977)
* Allow using a glob for include list.

* Update command flag.

* Make comment more clear.

* update comment.

* Allow scanning repo and org at the same time.
2022-12-16 13:28:16 -08:00
Gonçalo Silva e091fab94f Use Todoist's REST API v2 (#978)
v1 was deprecated on December 5, 2022.
2022-12-14 16:52:19 -08:00
Miccah 861ad057c7 Implement CustomRegex detector (#950)
* Remove verifying successRanges because it is unused in webhook

* Move custom_detectors validation code into its own file

* Initial implementation of custom regex detector

Secret verification is done via webhook.

* Add CustomRegex detector type

* Add upper bound to permutation

* Return early if the context is canceled

* Add headers from configuration

* Add detector name as a key in the JSON body

* Implement faster algorithm for productIndices
2022-12-14 10:26:53 -06:00
Bill Rich 36ca2601e0 Add s3 object count to trace logs (#975)
* Add s3 object count to trace logs

* fix debug level
2022-12-13 16:46:09 -08:00
Miccah 7ac7fdae44 Add more logging for git sources (#974) 2022-12-13 17:51:57 -06:00
dependabot[bot] 6dd0441f6c Bump github.com/envoyproxy/protoc-gen-validate from 0.6.13 to 0.9.1 (#963)
Bumps [github.com/envoyproxy/protoc-gen-validate](https://github.com/envoyproxy/protoc-gen-validate) from 0.6.13 to 0.9.1.
- [Release notes](https://github.com/envoyproxy/protoc-gen-validate/releases)
- [Changelog](https://github.com/bufbuild/protoc-gen-validate/blob/main/.goreleaser.yaml)
- [Commits](https://github.com/envoyproxy/protoc-gen-validate/compare/v0.6.13...v0.9.1)

---
updated-dependencies:
- dependency-name: github.com/envoyproxy/protoc-gen-validate
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-12 16:02:35 -08:00
dependabot[bot] a0b8edd987 Bump github.com/go-git/go-git/v5 from 5.4.2 to 5.5.1 (#972)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.4.2 to 5.5.1.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.4.2...v5.5.1)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-12 15:56:26 -08:00
ahrav 26befdd1ec [bug] - Handle error when scanning s3 bucket. (#969)
* Handle error when scanning s# bucket.

* move wait outside loop.

* Add logging.

* revert changes.

* remove.

* revert.
2022-12-12 10:10:06 -08:00
dependabot[bot] 4020c4002b Bump github.com/getsentry/sentry-go from 0.15.0 to 0.16.0 (#973)
Bumps [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) from 0.15.0 to 0.16.0.
- [Release notes](https://github.com/getsentry/sentry-go/releases)
- [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-go/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: github.com/getsentry/sentry-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-12 10:09:44 -08:00
dependabot[bot] aada296ddc Bump go.mongodb.org/mongo-driver from 1.11.0 to 1.11.1 (#971)
Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/mongodb/mongo-go-driver/releases)
- [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.0...v1.11.1)

---
updated-dependencies:
- dependency-name: go.mongodb.org/mongo-driver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-12 10:08:52 -08:00
Bill Rich f3249009f7 Bump github.com/bill-rich/disk-buffer-reader from v0.1.6 to v0.1.7 (#970) 2022-12-09 15:52:41 -08:00
dependabot[bot] 544359eee6 Bump github.com/xanzy/go-gitlab from 0.74.0 to 0.76.0 (#934)
Bumps [github.com/xanzy/go-gitlab](https://github.com/xanzy/go-gitlab) from 0.74.0 to 0.76.0.
- [Release notes](https://github.com/xanzy/go-gitlab/releases)
- [Changelog](https://github.com/xanzy/go-gitlab/blob/master/releases_test.go)
- [Commits](https://github.com/xanzy/go-gitlab/compare/v0.74.0...v0.76.0)

---
updated-dependencies:
- dependency-name: github.com/xanzy/go-gitlab
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-09 17:01:27 -06:00
Dustin Decker 7de9bdd12d Support globbing with ignore repos (#967) 2022-12-09 12:10:42 -08:00
ahrav a72b9feb35 Only scan org with --org flag. (#931) 2022-12-06 16:18:48 -08:00
dependabot[bot] f008d4bead Bump go.uber.org/zap from 1.23.0 to 1.24.0 (#955)
Bumps [go.uber.org/zap](https://github.com/uber-go/zap) from 1.23.0 to 1.24.0.
- [Release notes](https://github.com/uber-go/zap/releases)
- [Changelog](https://github.com/uber-go/zap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uber-go/zap/compare/v1.23.0...v1.24.0)

---
updated-dependencies:
- dependency-name: go.uber.org/zap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 15:38:28 -08:00
dependabot[bot] 6ee3000e53 Bump github.com/go-sql-driver/mysql from 1.6.0 to 1.7.0 (#954)
Bumps [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/go-sql-driver/mysql/releases)
- [Changelog](https://github.com/go-sql-driver/mysql/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-sql-driver/mysql/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/go-sql-driver/mysql
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 15:37:32 -08:00
Pulkit Aggarwal d96b7f8468 Update Adding_Detectors_external.md (#957) 2022-12-06 15:36:55 -08:00
Bill Rich 335ce85ce4 Export line number code (#962) 2022-12-06 15:31:15 -08:00
Bill Rich 4e2ce4f6fe Pre-commit not GH action PR (#961) 2022-12-06 14:32:25 -08:00
Bill Rich 33d32d2de4 Don't scan the --since-commit target (#960) 2022-12-06 13:24:27 -08:00
Bill Rich 1a1c2e275e Change chunker test source (#959)
* Change chunker test source

* Emit chunk if the size isn't 0
2022-12-06 12:45:08 -08:00
Bill Rich 9f99ee470d Integration test fixes (#956)
* Adjust repo count for new app

* Fix chunk test count
2022-12-06 08:42:24 -08:00
Dylan Ayrey c3e596e853 Update README.md (#952) 2022-12-02 12:09:25 -08:00
Miccah 2a2bcd93ac Add CustomRegex validation (#939)
* Add validation skeleton

* Add custom detector validation with tests

* Validate and test regex vars

* Implement RegexVarString

* Use RegexVarString for validating regex variables

* Add numerics to the regex variable matching

Co-authored-by: hxnyk <8292703+hxnyk@users.noreply.github.com>
2022-12-02 11:26:22 -06:00
Miccah 5a339b0ca1 Add test for configuring custom regex with webhook verification (#946) 2022-12-02 11:23:20 -06:00
Bill Rich 3b055ce3f9 Add logger to context (#947)
* Add logger to context

* Fatal on no org
2022-11-30 11:10:05 -08:00
Bill Rich c26142d898 Use new diskbufferreader version (#941) 2022-11-28 12:22:30 -08:00
Bill Rich f1ec9e74eb Close files to clean up tmp files (#940) 2022-11-22 13:13:34 -08:00
0xflotus 45ebafff61 fix: small errors (#917) 2022-11-22 09:01:52 -08:00
Bill Rich 79cae3b82b Add newlines when file is split (#937) 2022-11-22 09:01:39 -08:00
Dustin Decker 28dd25beeb S3 scanner improvements (#938) 2022-11-21 19:15:26 -08:00
Miccah 4409210b87 Add custom detectors configuration parsing (#927)
* Add custom_detectors proto

* Generate proto code

* Create custom_detectors package

Also create protoyaml package to test YAML unmarshalling the
configuration.

* Simplify custom_detectors proto by removing connection

* Generate proto code

* Update custom_detectors parsing tests
2022-11-21 15:10:38 -06:00
ahrav 054e98d108 Update slack webhook detector string check (#932)
* Update slack webhook detector check to text.

* remove redunant slashes.
2022-11-21 10:50:23 -08:00
Jessica 6e25664a52 add rambbitmq detector (#936)
* add rambbitmq detector

* use fixed length redaction

Co-authored-by: Dustin Decker <dustin@trufflesec.com>
2022-11-21 10:47:41 -08:00
Dustin Decker b45369cdbb Skip some FTP FPs (#929) 2022-11-21 06:52:21 -08:00
Thiago Lages de Alencar 9757c339d9 Fix error message (#933) 2022-11-20 05:31:11 -08:00
Dustin Decker ae4b387448 add LDAP detector (#896) 2022-11-18 19:45:11 -08:00
Dustin Decker b18edef01a Enable skipping of particular key IDs (#930)
* Enable skipping of particular key IDs

* update test
2022-11-18 09:09:40 -08:00
Miccah 87e7aa3970 Add warning about using trufflehog as a library (#928) 2022-11-18 09:30:51 -06:00
Miccah b3d3f531a4 Return an error from ReadToMax when it panics (#925) 2022-11-16 14:24:05 -06:00
ahrav eb4ff435a5 Use pointer to type. (#926) 2022-11-16 10:36:23 -08:00
ahrav b8be0a64a8 Use pointer to type. (#926) 2022-11-16 10:35:48 -08:00
dependabot[bot] d3b550a2e3 Bump cloud.google.com/go/secretmanager from 1.8.0 to 1.9.0 (#906)
Bumps [cloud.google.com/go/secretmanager](https://github.com/googleapis/google-cloud-go) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/googleapis/google-cloud-go/releases)
- [Changelog](https://github.com/googleapis/google-cloud-go/blob/main/documentai/CHANGES.md)
- [Commits](https://github.com/googleapis/google-cloud-go/compare/asset/v1.8.0...asset/v1.9.0)

---
updated-dependencies:
- dependency-name: cloud.google.com/go/secretmanager
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-16 08:48:45 -08:00
dependabot[bot] e5ba2c9a3c Bump github.com/getsentry/sentry-go from 0.14.0 to 0.15.0 (#914)
Bumps [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) from 0.14.0 to 0.15.0.
- [Release notes](https://github.com/getsentry/sentry-go/releases)
- [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-go/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: github.com/getsentry/sentry-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-16 08:47:38 -08:00
dependabot[bot] 1228127d12 Bump google-github-actions/auth from 0 to 1 (#912)
Bumps [google-github-actions/auth](https://github.com/google-github-actions/auth) from 0 to 1.
- [Release notes](https://github.com/google-github-actions/auth/releases)
- [Changelog](https://github.com/google-github-actions/auth/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google-github-actions/auth/compare/v0...v1)

---
updated-dependencies:
- dependency-name: google-github-actions/auth
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-16 08:46:26 -08:00
Miccah 86f9e1288f Initialize scan options if given a nil pointer (#924) 2022-11-15 17:01:59 -06:00
Miccah 696f5c68f4 Log the stack trace and recover object (#923)
* Log the stack trace and recover object

* Remove stderr log
2022-11-15 16:48:02 -06:00
Jessica 3d501975e4 Add filter as scan option to gitlab module's git scan (#919) 2022-11-15 13:02:37 -08:00
Ankush Goel 64cfe4d85e Update github_old.go (#916) 2022-11-15 10:40:55 -08:00
jcastilloixl 741130fb8d Update README.md (#920)
Fixing help subcommand reference to be `--help`, not `-h`, which does not work:

```
$ trufflehog git -h
trufflehog: error: unknown short flag '-h', try --help
$ trufflehog --version
trufflehog 3.16.4
```
2022-11-15 10:40:21 -08:00
Bill Rich d3b24fa592 Replace plain decoder with utf8 (#922) 2022-11-15 09:36:01 -08:00
Johann Saunier 42a82fc7e1 Update Scrapfly API Key Format (#910) 2022-11-11 15:24:17 -05:00
JJ Asghar aead0425d9 Fixed the brew install tap command (#911)
* Fixed the brew install tap command

It seems you need to run the full tap, not just trufflehog.

Signed-off-by: JJ Asghar <awesome@ibm.com>

* Update README.md

Signed-off-by: JJ Asghar <awesome@ibm.com>
Co-authored-by: Dustin Decker <dustin@trufflesec.com>
2022-11-11 15:21:47 -05:00
Ankush Goel bb0fa055dc fixed mailchimp detector (#909)
* fixed mailchimp detector

* Use sane http client

Co-authored-by: Dustin Decker <dustin@trufflesec.com>
2022-11-10 09:47:25 -05:00
kstilwell ecd25784f5 Adding Shopify detector (#875)
* Fixes/work based on testing

* Remove some commented code

* Change how verification happens and grab additional information

* Address linter warnings.

* add shopify detector to default detectors.

Co-authored-by: Dustin Decker <dustin@trufflesec.com>
Co-authored-by: Ahrav Dutta <ahravdutta02@gmail.com>
2022-11-08 16:21:57 -08:00
dependabot[bot] 02ed33d1c1 Bump go.mongodb.org/mongo-driver from 1.10.3 to 1.11.0 (#905) 2022-11-07 07:31:59 -08:00
Dustin Decker 5f0964add8 remove path for deduping URI 2022-11-06 08:12:46 -08:00
ahrav 28983036a0 only write if the filechunk has len > 0. (#903) 2022-11-05 18:19:41 -07:00
trufflesteeeve e8cd2e7fae Add Unknown visibility level (#902) 2022-11-04 14:28:20 -04:00
ahrav dd141fb55f [oc-147] - Add context to all git methods (#901)
* Add context to all git methods.

* remove logrus.

* Add ctx.

* Address comments.

* Add error to clone failing.

* Return error.
2022-11-03 16:36:52 -07:00
Dustin Decker 3a143f095b add option to skip tls verification for confluence (#900) 2022-11-03 13:10:01 -07:00
Miccah ab54ec4072 Check for closed channel in HandleFile (#895)
* Check for closed channel in HandleFile

* Refactor to be more readable

* Fix handler search
2022-11-02 16:35:19 -05:00
Miccah 85f5f3ea7b Add sqlserver integration test and some default params (#891)
* Improve anonymous redaction

* Add sqlserver integration test and some default params
2022-11-02 11:04:49 -05:00
Dustin Decker a7fc12240f Do local URI verification, while attempting to defuse SSRF (#879)
* simplify monogo pattern

* do URI verification locally, while attempting to defuse SSRF

* test SSRF defuse

* simplify err check logic per linter recommendation

* split up detectors

* address comments

* remove unused var
2022-11-01 17:27:24 -07:00
ahrav fe1e475a04 Prevent concurrent read and writes to visibility map. (#892) 2022-11-01 16:20:59 -07:00
dependabot[bot] 2884864cd3 Bump github.com/xanzy/go-gitlab from 0.73.1 to 0.74.0 (#886)
Bumps [github.com/xanzy/go-gitlab](https://github.com/xanzy/go-gitlab) from 0.73.1 to 0.74.0.
- [Release notes](https://github.com/xanzy/go-gitlab/releases)
- [Changelog](https://github.com/xanzy/go-gitlab/blob/master/releases_test.go)
- [Commits](https://github.com/xanzy/go-gitlab/compare/v0.73.1...v0.74.0)

---
updated-dependencies:
- dependency-name: github.com/xanzy/go-gitlab
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-31 13:01:29 -05:00
dependabot[bot] 2ac483da1f Bump github.com/TheZeroSlave/zapsentry from 1.11.0 to 1.12.0 (#887)
Bumps [github.com/TheZeroSlave/zapsentry](https://github.com/TheZeroSlave/zapsentry) from 1.11.0 to 1.12.0.
- [Release notes](https://github.com/TheZeroSlave/zapsentry/releases)
- [Commits](https://github.com/TheZeroSlave/zapsentry/compare/v1.11.0...v1.12.0)

---
updated-dependencies:
- dependency-name: github.com/TheZeroSlave/zapsentry
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-31 12:59:56 -05:00
ahrav fe029b1098 [THOG-793] - Return all unverified results (#856)
* Remove the check to filter and return only a single unverified result.

* Revert "Remove the check to filter and return only a single unverified result."

This reverts commit 494e432803.

* Add new CLI flag to filter unverified results.
2022-10-31 09:36:10 -07:00
Alexandr Marchenko c91158d8f7 remove updates check from github action (#883)
Because action uses the latest image there is no need to check for updates each time`
2022-10-31 09:34:33 -07:00
dependabot[bot] 6696c85d32 Bump cloud.google.com/go/secretmanager from 1.7.0 to 1.8.0 (#888)
Bumps [cloud.google.com/go/secretmanager](https://github.com/googleapis/google-cloud-go) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/googleapis/google-cloud-go/releases)
- [Changelog](https://github.com/googleapis/google-cloud-go/blob/main/documentai/CHANGES.md)
- [Commits](https://github.com/googleapis/google-cloud-go/compare/asset/v1.7.0...asset/v1.8.0)

---
updated-dependencies:
- dependency-name: cloud.google.com/go/secretmanager
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-31 08:59:58 -07:00
dependabot[bot] 4a458c9eab Bump github.com/mattn/go-sqlite3 from 1.14.15 to 1.14.16 (#889)
Bumps [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) from 1.14.15 to 1.14.16.
- [Release notes](https://github.com/mattn/go-sqlite3/releases)
- [Commits](https://github.com/mattn/go-sqlite3/compare/v1.14.15...v1.14.16)

---
updated-dependencies:
- dependency-name: github.com/mattn/go-sqlite3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-31 08:59:36 -07:00
Bill Rich 965279421c Support common ssh repo format (#878)
* Try ssh repo format

* Add tests
2022-10-28 11:56:03 -07:00
Bill Rich ab71b93f7d Add context to handler (#877)
* Add context to handler

* Return rather than break out of select
2022-10-28 08:57:55 -07:00
Bill Rich 034ca4fb5b Add bytes counter to scans (#876) 2022-10-27 12:54:22 -07:00
Dustin Decker 0c81cba918 remove noisy logging in sqlserver detector 2022-10-26 18:12:26 -07:00
Dustin Decker ca8a5ef741 increase digitalocean token sensitivity (#872) 2022-10-26 08:22:21 -07:00
Dustin Decker 4f83dd816d increase datadog token sensitivity (#871) 2022-10-26 08:22:10 -07:00
Dustin Decker 33c6c193e3 improve fastly validation endpoint and add extra data (#870) 2022-10-26 08:22:03 -07:00
Dustin Decker 466b9e2d6b only detect live env razor pay and use std lib (#869)
* only detect live env razor pay and use std lib

* fix shadowed var
2022-10-26 08:13:13 -07:00
Dustin Decker dac40519e4 support github fine grained tokens and add extra data (#868)
* support github fine grained tokens and add extra data

* fix shadowed var
2022-10-26 08:13:02 -07:00
Alexandr Marchenko 60464da3ce proposal: SqlServer connection string detector (#867)
* sqlserver added to detectors.proto

* make protos

* boilerplate detector generated

* wireup

* initial
2022-10-26 07:46:13 -07:00
Bill Rich d7d614cc5f Copy buffer bytes (#864) 2022-10-25 09:09:47 -07:00
Dylan Ayrey d5fef0f3e1 Update README.md (#861) 2022-10-24 14:11:14 -07:00
Bill Rich 958266ea84 Run chunker in pipeline (#859)
* Run chunker in pipeline

* Move ChunkSize and PeekSize to source package.

* Use new Chunk and Peek size location
2022-10-24 13:57:27 -07:00
Bill Rich 3d5f697f9a Use line aware chunking for git. (#858) 2022-10-24 13:00:03 -07:00
dependabot[bot] 2a58268e42 Bump github.com/stretchr/testify from 1.8.0 to 1.8.1 (#860)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-24 10:05:16 -07:00
Dustin Decker 64ace363af Change commit to trace level logging 2022-10-24 08:59:52 -07:00
dependabot[bot] ce36383513 Bump github.com/getsentry/sentry-go from 0.13.0 to 0.14.0 (#839)
Bumps [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) from 0.13.0 to 0.14.0.
- [Release notes](https://github.com/getsentry/sentry-go/releases)
- [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-go/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: github.com/getsentry/sentry-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-23 19:03:18 -07:00
dependabot[bot] 2e1006e1a7 Bump github.com/denisenkom/go-mssqldb from 0.12.2 to 0.12.3 (#851)
Bumps [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) from 0.12.2 to 0.12.3.
- [Release notes](https://github.com/denisenkom/go-mssqldb/releases)
- [Commits](https://github.com/denisenkom/go-mssqldb/compare/v0.12.2...v0.12.3)

---
updated-dependencies:
- dependency-name: github.com/denisenkom/go-mssqldb
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-23 19:02:27 -07:00
dependabot[bot] 477b461e18 Bump go.mongodb.org/mongo-driver from 1.10.2 to 1.10.3 (#837)
Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.10.2 to 1.10.3.
- [Release notes](https://github.com/mongodb/mongo-go-driver/releases)
- [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.10.2...v1.10.3)

---
updated-dependencies:
- dependency-name: go.mongodb.org/mongo-driver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-21 12:49:06 -07:00
dependabot[bot] 68740953cd Bump github.com/envoyproxy/protoc-gen-validate from 0.6.8 to 0.6.13 (#838)
Bumps [github.com/envoyproxy/protoc-gen-validate](https://github.com/envoyproxy/protoc-gen-validate) from 0.6.8 to 0.6.13.
- [Release notes](https://github.com/envoyproxy/protoc-gen-validate/releases)
- [Commits](https://github.com/envoyproxy/protoc-gen-validate/compare/v0.6.8...v0.6.13)

---
updated-dependencies:
- dependency-name: github.com/envoyproxy/protoc-gen-validate
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-21 12:48:20 -07:00
ahrav 46bc010165 Add tests for including github repos. (#854) 2022-10-21 07:56:36 -07:00
834 changed files with 7032 additions and 2841 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
with:
go-version: '1.18'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
uses: goreleaser/goreleaser-action@v4
with:
distribution: goreleaser
version: latest
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- id: 'auth'
uses: 'google-github-actions/auth@v0'
uses: 'google-github-actions/auth@v1'
with:
workload_identity_provider: 'projects/811013774421/locations/global/workloadIdentityPools/github-pool/providers/github-provider'
service_account: 'github-ci-external@trufflehog-testing.iam.gserviceaccount.com'
@@ -42,7 +42,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- id: 'auth'
uses: 'google-github-actions/auth@v0'
uses: 'google-github-actions/auth@v1'
with:
workload_identity_provider: 'projects/811013774421/locations/global/workloadIdentityPools/github-pool/providers/github-provider'
service_account: 'github-ci-external@trufflehog-testing.iam.gserviceaccount.com'
+1 -1
View File
@@ -9,7 +9,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o trufflehog .
FROM alpine:3.15
RUN apk add --no-cache git ca-certificates \
RUN apk add --no-cache bash git ca-certificates \
&& rm -rf /var/cache/apk/* && \
update-ca-certificates
COPY --from=builder /build/trufflehog /usr/bin/trufflehog
+1 -1
View File
@@ -1,6 +1,6 @@
FROM alpine:3.15
RUN apk add --no-cache git ca-certificates \
RUN apk add --no-cache bash git ca-certificates \
&& rm -rf /var/cache/apk/* && \
update-ca-certificates
WORKDIR /usr/bin/
+179 -21
View File
@@ -31,12 +31,72 @@ https://join.slack.com/t/trufflehog-community/shared_invite/zt-pw2qbi43-Aa86hkii
docker run -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --org=trufflesecurity
```
## Examples
### Example 1: Scan a repo for only verified secrets
Command:
```
trufflehog git https://github.com/trufflesecurity/test_keys --only-verified
```
Expected output:
```
🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷
Found verified result 🐷🔑
Detector Type: AWS
Decoder Type: PLAIN
Raw result: AKIAYVP4CIPPERUVIFXG
Line: 4
Commit: fbc14303ffbf8fb1c2c1914e8dda7d0121633aca
File: keys
Email: counter <counter@counters-MacBook-Air.local>
Repository: https://github.com/trufflesecurity/test_keys
Timestamp: 2022-06-16 10:17:40 -0700 PDT
...
```
### Example 2: Scan a GitHub Org for only verified secrets
```
trufflehog github --org=trufflesecurity --only-verified
```
### Example 3: Scan a GitHub Repo for only verified keys and get JSON output
Command:
```
trufflehog git https://github.com/trufflesecurity/test_keys --only-verified --json
```
Expected output:
```
{"SourceMetadata":{"Data":{"Git":{"commit":"fbc14303ffbf8fb1c2c1914e8dda7d0121633aca","file":"keys","email":"counter \u003ccounter@counters-MacBook-Air.local\u003e","repository":"https://github.com/trufflesecurity/test_keys","timestamp":"2022-06-16 10:17:40 -0700 PDT","line":4}}},"SourceID":0,"SourceType":16,"SourceName":"trufflehog - git","DetectorType":2,"DetectorName":"AWS","DecoderName":"PLAIN","Verified":true,"Raw":"AKIAYVP4CIPPERUVIFXG","Redacted":"AKIAYVP4CIPPERUVIFXG","ExtraData":{"account":"595918472158","arn":"arn:aws:iam::595918472158:user/canarytokens.com@@mirux23ppyky6hx3l6vclmhnj","user_id":"AIDAYVP4CIPPJ5M54LRCY"},"StructuredData":null}
...
```
### Example 4: Scan an S3 bucket for verified keys
```
trufflehog s3 --bucket=<bucket name> --only-verified
```
### FAQ
+ All I see is `🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷` and the program exits, what gives?
+ That means no secrets were detected
+ Why is the scan is taking a long time when I scan a GitHub org
+ Unauthenticated GitHub scans have rate limits. To improve your rate limits, include the `--token` flag with a personal access token
+ It says a private key was verified, what does that mean?
+ Check out our Driftwood blog post to learn how to do this, in short we've confirmed the key can be used live for SSH or SSL [Blog post](https://trufflesecurity.com/blog/driftwood-know-if-private-keys-are-sensitive/)
# What's new in v3?
TruffleHog v3 is a complete rewrite in Go with many new powerful features.
- We've **added over 700 credential detectors that support active verification against their respective APIs**.
- We've also added native **support for scanning GitHub, GitLab, filesystems, and S3**.
- We've also added native **support for scanning GitHub, GitLab, filesystems, S3, and Circle CI**.
- **Instantly verify private keys** against millions of github users and **billions** of TLS certificates using our [Driftwood](https://trufflesecurity.com/blog/driftwood) technology.
@@ -70,7 +130,7 @@ docker run -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --repo ht
#### **Apple M1 users**
The `linux/arm64` image is better to run on the M1 than the amd64 image.
Even better is running the native darwin binary avilable, but there is not container image for that.
Even better is running the native darwin binary available, but there is no container image for that.
```bash
docker run --platform linux/arm64 -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --repo https://github.com/trufflesecurity/test_keys
@@ -87,8 +147,7 @@ Help with setting up this packaging would be appreciated!
### 5. Brew
```bash
brew tap trufflesecurity/trufflehog
brew install trufflehog
brew install trufflesecurity/trufflehog/trufflehog
```
## Usage
@@ -101,9 +160,10 @@ TruffleHog has a sub-command for each source of data that you may want to scan:
- S3
- filesystem
- syslog
- circleci
- file and stdin (coming soon)
Each subcommand can have options that you can see with the `-h` flag provided to the sub command:
Each subcommand can have options that you can see with the `--help` flag provided to the sub command:
```
$ trufflehog git --help
@@ -113,26 +173,19 @@ Find credentials in git repositories.
Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
--debug Run in debug mode
--version Prints trufflehog version.
--debug Run in debug mode.
--trace Run in trace mode.
-j, --json Output in JSON format.
--json-legacy Use the pre-v3.0 JSON format. Only works with git, gitlab, and github sources.
--concurrency=1 Number of concurrent workers.
--concurrency=10 Number of concurrent workers.
--no-verification Don't verify the results.
--only-verified Only output verified results.
--filter-unverified Only output first unverified result per chunk per detector if there are more than one results.
--config=CONFIG Path to configuration file.
--print-avg-detector-time Print the average time spent on each detector.
--no-update Don't check for updates.
-i, --include-paths=INCLUDE-PATHS
Path to file with newline separated regexes for files to include in scan.
-x, --exclude-paths=EXCLUDE-PATHS
Path to file with newline separated regexes for files to exclude in scan.
--since-commit=SINCE-COMMIT
Commit to start scan from.
--branch=BRANCH Branch to scan.
--max-depth=MAX-DEPTH Maximum depth of commits to scan.
--allow No-op flag for backwards compat.
--entropy No-op flag for backwards compat.
--regex No-op flag for backwards compat.
--fail Exit with code 183 if results are found.
--version Show application version.
Args:
<uri> Git repository URL. https://, file://, or ssh:// schema expected.
@@ -209,13 +262,111 @@ repos:
- id: trufflehog
name: TruffleHog
description: Detect secrets in your data.
entry: bash -c 'trufflehog git file://. --only-verified --fail'
entry: bash -c 'trufflehog git file://. --since-commit HEAD --only-verified --fail'
# For running trufflehog in docker, use the following entry instead:
# entry: bash -c 'docker run -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --only-verified --fail'
# entry: bash -c 'docker run -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --since-commit HEAD --only-verified --fail'
language: system
stages: ["commit", "push"]
```
## Regex Detector (alpha)
Trufflehog supports detection and verification of custom regular expressions.
For detection, at least one **regular expression** and **keyword** is required.
A **keyword** is a fixed literal string identifier that appears in or around
the regex to be detected. To allow maximum flexibility for verification, a
webhook is used containing the regular expression matches.
Trufflehog will send a JSON POST request containing the regex matches to a
configured webhook endpoint. If the endpoint responds with a `200 OK` response
status code, the secret is considered verified.
**NB:** This feature is alpha and subject to change.
### Regex Detector Example
```yaml
# config.yaml
detectors:
- name: hog detector
keywords:
- hog
regex:
adjective: hogs are (\S+)
verify:
- endpoint: http://localhost:8000/
# unsafe must be set if the endpoint is HTTP
unsafe: true
headers:
- 'Authorization: super secret authorization header'
```
```
» trufflehog filesystem --directory /tmp --config config.yaml --only-verified
🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷
Found verified result 🐷🔑
Detector Type: CustomRegex
Decoder Type: PLAIN
Raw result: hogs are cool
File: /tmp/hog-facts.txt
```
#### Verification Server Example (Python)
Unless you run a verification server, secrets found by the custom regex
detector will be unverified. Here is an example Python implementation of a
verification server for the above `config.yaml` file.
```python
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
AUTH_HEADER = 'super secret authorization header'
class Verifier(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(405)
self.end_headers()
def do_POST(self):
try:
if self.headers['Authorization'] != AUTH_HEADER:
self.send_response(401)
self.end_headers()
return
# read the body
length = int(self.headers['Content-Length'])
request = json.loads(self.rfile.read(length))
self.log_message("%s", request)
# check the match
if request['hog detector']['adjective'][-1] == 'cool':
self.send_response(200)
self.end_headers()
else:
# any other response besides 200
self.send_response(406)
self.end_headers()
except Exception:
self.send_response(400)
self.end_headers()
with HTTPServer(('', 8000), Verifier) as server:
try:
server.serve_forever()
except KeyboardInterrupt:
pass
```
## Use as a library
Currently, trufflehog is in heavy development and no guarantees can be made on
the stability of the public APIs at this time.
## Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
@@ -239,3 +390,10 @@ We have published some [documentation and tooling to get started on adding new s
## License Change
Since v3.0, TruffleHog is released under a AGPL 3 license, included in [`LICENSE`](LICENSE). TruffleHog v3.0 uses none of the previous codebase, but care was taken to preserve backwards compatibility on the command line interface. The work previous to this release is still available licensed under GPL 2.0 in the history of this repository and the previous package releases and tags. A completed CLA is required for us to accept contributions going forward.
## Enterprise product
Are you interested in continously monitoring your Git, Jira, Slack, Confluence, etc.. for credentials? We have an enterprise product that can help. Reach out here to learn more https://trufflesecurity.com/contact/
We take the revenue from the enterprise product to fund more awesome open source projects that the whole community can benefit from.
+3 -1
View File
@@ -8,7 +8,8 @@ inputs:
required: true
base:
description: Start scanning from here (usually main branch).
required: true
required: false
default: ''
head:
description: Scan commits until here (usually dev branch).
required: false
@@ -30,4 +31,5 @@ runs:
- --branch
- ${{ inputs.head }}
- --fail
- --no-update
- ${{ inputs.extra_args }}
+5 -4
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env ash
#!/usr/bin/env bash
# `$*` expands the `args` supplied in an `array` individually
# or splits `args` in a string separated by whitespace.
/usr/bin/trufflehog $*
# Parse the last argument into an array of extra_args.
mapfile -t extra_args < <(bash -c "for arg in ${*: -1}; do echo \$arg; done")
/usr/bin/trufflehog "${@: 1: $#-1}" "${extra_args[@]}"
+56 -43
View File
@@ -9,63 +9,66 @@ replace github.com/zricethezav/gitleaks/v8 => github.com/trufflesecurity/gitleak
replace github.com/gitleaks/go-gitdiff => github.com/trufflesecurity/go-gitdiff v0.7.6-zombies2
require (
cloud.google.com/go/secretmanager v1.7.0
cloud.google.com/go/secretmanager v1.10.0
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
github.com/TheZeroSlave/zapsentry v1.11.0
github.com/TheZeroSlave/zapsentry v1.12.0
github.com/aws/aws-sdk-go v1.44.83
github.com/bill-rich/disk-buffer-reader v0.1.4
github.com/bill-rich/disk-buffer-reader v0.1.7
github.com/bill-rich/go-syslog v0.0.0-20220413021637-49edb52a574c
github.com/bitfinexcom/bitfinex-api-go v0.0.0-20210608095005-9e0b26f200fb
github.com/bradleyfalzon/ghinstallation/v2 v2.1.0
github.com/crewjam/rfc5424 v0.1.0
github.com/denisenkom/go-mssqldb v0.12.2
github.com/envoyproxy/protoc-gen-validate v0.6.8
github.com/denisenkom/go-mssqldb v0.12.3
github.com/envoyproxy/protoc-gen-validate v0.9.1
github.com/fatih/color v1.13.0
github.com/felixge/fgprof v0.9.3
github.com/getsentry/sentry-go v0.13.0
github.com/getsentry/sentry-go v0.16.0
github.com/go-errors/errors v1.4.2
github.com/go-git/go-git/v5 v5.4.2
github.com/go-git/go-git/v5 v5.5.2
github.com/go-ldap/ldap/v3 v3.4.4
github.com/go-logr/logr v1.2.3
github.com/go-logr/zapr v1.2.3
github.com/go-sql-driver/mysql v1.6.0
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-sql-driver/mysql v1.7.0
github.com/gobwas/glob v0.2.3
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/go-cmp v0.5.9
github.com/google/go-github/v42 v42.0.0
github.com/gorilla/mux v1.8.0
github.com/h2non/filetype v1.1.3
github.com/hashicorp/go-retryablehttp v0.7.1
github.com/hashicorp/go-retryablehttp v0.7.2
github.com/jlaffaye/ftp v0.1.0
github.com/joho/godotenv v1.4.0
github.com/jpillora/overseer v1.1.6
github.com/kylelemons/godebug v1.1.0
github.com/lib/pq v1.10.7
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-sqlite3 v1.14.15
github.com/mattn/go-sqlite3 v1.14.16
github.com/mholt/archiver/v4 v4.0.0-alpha.7
github.com/paulbellamy/ratecounter v0.2.0
github.com/pkg/errors v0.9.1
github.com/razorpay/razorpay-go v0.0.0-20210728161131-0341409a6ab2
github.com/rs/zerolog v1.28.0
github.com/rabbitmq/amqp091-go v1.5.0
github.com/sergi/go-diff v1.2.0
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.0
github.com/stretchr/testify v1.8.1
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502
github.com/xanzy/go-gitlab v0.73.1
go.mongodb.org/mongo-driver v1.10.2
go.uber.org/zap v1.23.0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
github.com/xanzy/go-gitlab v0.77.0
go.mongodb.org/mongo-driver v1.11.1
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.5.0
golang.org/x/exp v0.0.0-20221018205818-5c77f4b2bbd7
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783
golang.org/x/sync v0.1.0
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c
google.golang.org/protobuf v1.28.1
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/h2non/gock.v1 v1.1.2
sigs.k8s.io/yaml v1.3.0
)
require (
cloud.google.com/go v0.104.0 // indirect
cloud.google.com/go/compute v1.7.0 // indirect
cloud.google.com/go/iam v0.3.0 // indirect
cloud.google.com/go/compute v1.12.1 // indirect
cloud.google.com/go/compute/metadata v0.2.1 // indirect
cloud.google.com/go/iam v0.8.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
@@ -73,19 +76,22 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/benbjohnson/clock v1.1.0 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-git/go-billy/v5 v5.4.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/golang-jwt/jwt/v4 v4.4.1 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
@@ -96,44 +102,51 @@ require (
github.com/google/go-github/v45 v45.2.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
github.com/googleapis/gax-go/v2 v2.5.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jpillora/s3 v1.1.4 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/klauspost/compress v1.15.5 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.15.11 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.23.0 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/xanzy/ssh-agent v0.3.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.23.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect
golang.org/x/sys v0.0.0-20220908150016-7ac13a9a928d // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
golang.org/x/tools v0.1.12 // indirect
google.golang.org/api v0.96.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
golang.org/x/tools v0.3.0 // indirect
google.golang.org/api v0.103.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/grpc v1.49.0 // indirect
google.golang.org/grpc v1.51.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+161 -616
View File
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -49,8 +49,12 @@ If you think that something should be included outside of these guidelines, plea
```bash
go run hack/generate/generate.go detector <DetectorType enum name>
```
3. Add Secret Scanner
3. Complete the secret detector.
Add the secret scanner to the `pkg/engine/defaults.go` file like `github.com/trufflesecurity/trufflehog/v3/pkg/detectors/<detector_name>` and
`<detector_name>.Scanner{},`
4. Complete the secret detector.
The previous step templated a boilerplate + some example code as a package in the `pkg/detectors` folder for you to work on.
The secret detector can be completed with these general steps:
@@ -104,4 +108,4 @@ Now, the detector test should attempt to load the given env key from that file.
### Testing a detector
```bash
go test ./pkg/detectors/<detector> -tags=detectors
```
```
+2 -4
View File
@@ -156,15 +156,13 @@ func main() {
}
for _, repo := range strings.Split(*scanCmdRepo, ",") {
if err := sem.Acquire(ctx, 1); err != nil {
log.Fatal(err)
}
sem.Acquire(ctx, 1)
wgChunkers.Add(1)
go func(r string) {
defer sem.Release(1)
defer wgChunkers.Done()
log.Infof("cloning %s", r)
path, repo, err := git.CloneRepoUsingUnauthenticated(r)
path, repo, err := git.CloneRepoUsingUnauthenticated(ctx, r)
if err != nil {
log.Fatal(err)
}
+57 -17
View File
@@ -2,7 +2,6 @@ package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"os"
@@ -19,9 +18,11 @@ import (
"gopkg.in/alecthomas/kingpin.v2"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/config"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
"github.com/trufflesecurity/trufflehog/v3/pkg/decoders"
"github.com/trufflesecurity/trufflehog/v3/pkg/engine"
"github.com/trufflesecurity/trufflehog/v3/pkg/log"
"github.com/trufflesecurity/trufflehog/v3/pkg/output"
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
"github.com/trufflesecurity/trufflehog/v3/pkg/sources/git"
@@ -30,15 +31,17 @@ import (
)
var (
cli = kingpin.New("TruffleHog", "TruffleHog is a tool for finding credentials.")
cmd string
debug = cli.Flag("debug", "Run in debug mode.").Bool()
trace = cli.Flag("trace", "Run in trace mode.").Bool()
jsonOut = cli.Flag("json", "Output in JSON format.").Short('j').Bool()
jsonLegacy = cli.Flag("json-legacy", "Use the pre-v3.0 JSON format. Only works with git, gitlab, and github sources.").Bool()
concurrency = cli.Flag("concurrency", "Number of concurrent workers.").Default(strconv.Itoa(runtime.NumCPU())).Int()
noVerification = cli.Flag("no-verification", "Don't verify the results.").Bool()
onlyVerified = cli.Flag("only-verified", "Only output verified results.").Bool()
cli = kingpin.New("TruffleHog", "TruffleHog is a tool for finding credentials.")
cmd string
debug = cli.Flag("debug", "Run in debug mode.").Bool()
trace = cli.Flag("trace", "Run in trace mode.").Bool()
jsonOut = cli.Flag("json", "Output in JSON format.").Short('j').Bool()
jsonLegacy = cli.Flag("json-legacy", "Use the pre-v3.0 JSON format. Only works with git, gitlab, and github sources.").Bool()
concurrency = cli.Flag("concurrency", "Number of concurrent workers.").Default(strconv.Itoa(runtime.NumCPU())).Int()
noVerification = cli.Flag("no-verification", "Don't verify the results.").Bool()
onlyVerified = cli.Flag("only-verified", "Only output verified results.").Bool()
filterUnverified = cli.Flag("filter-unverified", "Only output first unverified result per chunk per detector if there are more than one results.").Bool()
configFilename = cli.Flag("config", "Path to configuration file.").ExistingFile()
// rules = cli.Flag("rules", "Path to file with custom rules.").String()
printAvgDetectorTime = cli.Flag("print-avg-detector-time", "Print the average time spent on each detector.").Bool()
noUpdate = cli.Flag("no-update", "Don't check for updates.").Bool()
@@ -62,12 +65,16 @@ var (
githubScanToken = githubScan.Flag("token", "GitHub token. Can be provided with environment variable GITHUB_TOKEN.").Envar("GITHUB_TOKEN").String()
githubIncludeForks = githubScan.Flag("include-forks", "Include forks in scan.").Bool()
githubIncludeMembers = githubScan.Flag("include-members", "Include organization member repositories in scan.").Bool()
githubIncludeRepos = githubScan.Flag("include-repos", `Repositories to include in an org scan. This can also be a glob pattern. You can repeat this flag. Must use Github repo full name. Example: "trufflesecurity/trufflehog", "trufflesecurity/t*"`).Strings()
githubExcludeRepos = githubScan.Flag("exclude-repos", `Repositories to exclude in an org scan. This can also be a glob pattern. You can repeat this flag. Must use Github repo full name. Example: "trufflesecurity/driftwood", "trufflesecurity/d*"`).Strings()
gitlabScan = cli.Command("gitlab", "Find credentials in GitLab repositories.")
// TODO: Add more GitLab options
gitlabScanEndpoint = gitlabScan.Flag("endpoint", "GitLab endpoint.").Default("https://gitlab.com").String()
gitlabScanRepos = gitlabScan.Flag("repo", "GitLab repo url. You can repeat this flag. Leave empty to scan all repos accessible with provided credential. Example: https://gitlab.com/org/repo.git").Strings()
gitlabScanToken = gitlabScan.Flag("token", "GitLab token. Can be provided with environment variable GITLAB_TOKEN.").Envar("GITLAB_TOKEN").Required().String()
gitlabScanEndpoint = gitlabScan.Flag("endpoint", "GitLab endpoint.").Default("https://gitlab.com").String()
gitlabScanRepos = gitlabScan.Flag("repo", "GitLab repo url. You can repeat this flag. Leave empty to scan all repos accessible with provided credential. Example: https://gitlab.com/org/repo.git").Strings()
gitlabScanToken = gitlabScan.Flag("token", "GitLab token. Can be provided with environment variable GITLAB_TOKEN.").Envar("GITLAB_TOKEN").Required().String()
gitlabScanIncludePaths = gitlabScan.Flag("include-paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String()
gitlabScanExcludePaths = gitlabScan.Flag("exclude-paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String()
filesystemScan = cli.Command("filesystem", "Find credentials in a filesystem.")
filesystemDirectories = filesystemScan.Flag("directory", "Path to directory to scan. You can repeat this flag.").Required().Strings()
@@ -88,6 +95,9 @@ var (
syslogTLSCert = syslogScan.Flag("cert", "Path to TLS cert.").String()
syslogTLSKey = syslogScan.Flag("key", "Path to TLS key.").String()
syslogFormat = syslogScan.Flag("format", "Log format. Can be rfc3164 or rfc5424").String()
circleCiScan = cli.Command("circleci", "Scan CircleCI")
circleCiScanToken = circleCiScan.Flag("token", "CircleCI token. Can also be provided with environment variable").Envar("CIRCLECI_TOKEN").Required().String()
)
func init() {
@@ -107,9 +117,11 @@ func init() {
}
switch {
case *trace:
log.SetLevel(5)
logrus.SetLevel(logrus.TraceLevel)
logrus.Debugf("running version %s", version.BuildVersion)
case *debug:
log.SetLevel(2)
logrus.SetLevel(logrus.DebugLevel)
logrus.Debugf("running version %s", version.BuildVersion)
default:
@@ -141,7 +153,7 @@ func main() {
func run(state overseer.State) {
if *debug {
fmt.Println("trufflehog " + version.BuildVersion)
logrus.Debugf("trufflehog %s", version.BuildVersion)
}
if *githubScanToken != "" {
@@ -166,12 +178,27 @@ func run(state overseer.State) {
}
}()
}
logger, sync := log.New("trufflehog", log.WithConsoleSink(os.Stderr))
context.SetDefaultLogger(logger)
defer func() { _ = sync() }()
conf := &config.Config{}
if *configFilename != "" {
var err error
conf, err = config.Read(*configFilename)
if err != nil {
logger.Error(err, "error parsing the provided configuration file")
os.Exit(1)
}
}
ctx := context.TODO()
e := engine.Start(ctx,
engine.WithConcurrency(*concurrency),
engine.WithDecoders(decoders.DefaultDecoders()...),
engine.WithDetectors(!*noVerification, engine.DefaultDetectors()...),
engine.WithDetectors(!*noVerification, conf.Detectors...),
engine.WithFilterUnverified(*filterUnverified),
)
filter, err := common.FilterFromFiles(*gitScanIncludePaths, *gitScanExcludePaths)
@@ -183,7 +210,7 @@ func run(state overseer.State) {
var remote bool
switch cmd {
case gitScan.FullCommand():
repoPath, remote, err = git.PrepareRepoSinceCommit(*gitScanURI, *gitScanSinceCommit)
repoPath, remote, err = git.PrepareRepoSinceCommit(ctx, *gitScanURI, *gitScanSinceCommit)
if err != nil || repoPath == "" {
logrus.WithError(err).Fatal("error preparing git repo for scanning")
}
@@ -204,7 +231,7 @@ func run(state overseer.State) {
}
case githubScan.FullCommand():
if len(*githubScanOrgs) == 0 && len(*githubScanRepos) == 0 {
log.Fatal("You must specify at least one organization or repository.")
logrus.Fatal("You must specify at least one organization or repository.")
}
github := func(c *sources.Config) {
@@ -215,16 +242,24 @@ func run(state overseer.State) {
c.IncludeForks = *githubIncludeForks
c.IncludeMembers = *githubIncludeMembers
c.Concurrency = *concurrency
c.ExcludeRepos = *githubExcludeRepos
c.IncludeRepos = *githubIncludeRepos
}
if err = e.ScanGitHub(ctx, sources.NewConfig(github)); err != nil {
logrus.WithError(err).Fatal("Failed to scan Github.")
}
case gitlabScan.FullCommand():
filter, err := common.FilterFromFiles(*gitlabScanIncludePaths, *gitlabScanExcludePaths)
if err != nil {
logrus.WithError(err).Fatal("could not create filter")
}
gitlab := func(c *sources.Config) {
c.Endpoint = *gitlabScanEndpoint
c.Token = *gitlabScanToken
c.Repos = *gitlabScanRepos
c.Filter = filter
}
if err = e.ScanGitLab(ctx, sources.NewConfig(gitlab)); err != nil {
@@ -261,6 +296,10 @@ func run(state overseer.State) {
if err = e.ScanSyslog(ctx, sources.NewConfig(syslog)); err != nil {
logrus.WithError(err).Fatal("Failed to scan syslog.")
}
case circleCiScan.FullCommand():
if err = e.ScanCircleCI(ctx, *circleCiScanToken); err != nil {
logrus.WithError(err).Fatal("Failed to scan CircleCI.")
}
}
// asynchronously wait for scanning to finish and cleanup
go e.Finish(ctx)
@@ -280,7 +319,7 @@ func run(state overseer.State) {
switch {
case *jsonLegacy:
output.PrintLegacyJSON(&r)
output.PrintLegacyJSON(ctx, &r)
case *jsonOut:
output.PrintJSON(&r)
default:
@@ -288,6 +327,7 @@ func run(state overseer.State) {
}
}
logrus.Debugf("scanned %d chunks", e.ChunksScanned())
logrus.Debugf("scanned %d bytes", e.BytesScanned())
if *printAvgDetectorTime {
printAverageDetectorTime(e)
-36
View File
@@ -1,36 +0,0 @@
package common
import (
"bufio"
"errors"
"io"
log "github.com/sirupsen/logrus"
)
const (
ChunkSize = 10 * 1024
PeekSize = 3 * 1024
)
func ChunkReader(r io.Reader) chan []byte {
chunkChan := make(chan []byte)
go func() {
defer close(chunkChan)
reader := bufio.NewReaderSize(bufio.NewReader(r), ChunkSize)
for {
chunk := make([]byte, ChunkSize)
n, err := reader.Read(chunk)
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Error chunking reader.")
break
}
peekData, _ := reader.Peek(PeekSize)
chunkChan <- append(chunk[:n], peekData...)
if errors.Is(err, io.EOF) {
break
}
}
}()
return chunkChan
}
+4 -3
View File
@@ -9,6 +9,7 @@ import (
"time"
"github.com/hashicorp/go-retryablehttp"
"github.com/sirupsen/logrus"
)
var caCerts = []string{
@@ -93,7 +94,7 @@ func NewCustomTransport(T http.RoundTripper) *CustomTransport {
func PinnedRetryableHttpClient() *http.Client {
httpClient := retryablehttp.NewClient()
httpClient.Logger = nil
httpClient.Logger = logrus.WithField("name", "PinnedRetryableHttpClient").WithField("level", logrus.DebugLevel)
httpClient.HTTPClient.Transport = NewCustomTransport(&http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: PinnedCertPool(),
@@ -115,7 +116,7 @@ func PinnedRetryableHttpClient() *http.Client {
func RetryableHttpClient() *http.Client {
httpClient := retryablehttp.NewClient()
httpClient.RetryMax = 3
httpClient.Logger = nil
httpClient.Logger = logrus.WithField("name", "RetryableHttpClient").WithField("level", logrus.DebugLevel)
httpClient.HTTPClient.Timeout = 3 * time.Second
httpClient.HTTPClient.Transport = NewCustomTransport(nil)
return httpClient.StandardClient()
@@ -124,7 +125,7 @@ func RetryableHttpClient() *http.Client {
func RetryableHttpClientTimeout(timeOutSeconds int64) *http.Client {
httpClient := retryablehttp.NewClient()
httpClient.RetryMax = 3
httpClient.Logger = nil
httpClient.Logger = logrus.WithField("name", "RetryableHttpClientTimeout").WithField("level", logrus.DebugLevel)
httpClient.HTTPClient.Timeout = time.Duration(timeOutSeconds) * time.Second
httpClient.HTTPClient.Transport = NewCustomTransport(nil)
return httpClient.StandardClient()
+8 -2
View File
@@ -18,7 +18,10 @@ func Recover(ctx context.Context) {
if eventID := sentry.CurrentHub().Recover(err); eventID != nil {
ctx.Logger().Info("panic captured", "event_id", *eventID)
}
fmt.Fprint(os.Stderr, panicStack)
ctx.Logger().Error(fmt.Errorf("panic"), "recovered from panic",
"stack-trace", panicStack,
"recover", err,
)
if !sentry.Flush(time.Second * 5) {
ctx.Logger().Info("sentry flush failed")
}
@@ -32,7 +35,10 @@ func RecoverWithExit(ctx context.Context) {
if eventID := sentry.CurrentHub().Recover(err); eventID != nil {
ctx.Logger().Info("panic captured", "event_id", *eventID)
}
fmt.Fprint(os.Stderr, panicStack)
ctx.Logger().Error(fmt.Errorf("panic"), "recovered from panic before exiting",
"stack-trace", panicStack,
"recover", err,
)
if !sentry.Flush(time.Second * 5) {
ctx.Logger().Info("sentry flush failed")
}
+2 -10
View File
@@ -2,22 +2,14 @@ package common
import (
"path/filepath"
"github.com/h2non/filetype"
)
var (
KB, MB, GB, TB, PB = 1e3, 1e6, 1e9, 1e12, 1e15
IGNORED_EXTENSIONS = []string{"pdf", "mp4", "avi", "mpeg", "mpg", "mov", "wmv", "m4p", "swf", "mp2", "flv", "vob", "webm", "hdv", "3gp", "ogg", "mp3", "wav", "flac", "tif", "tiff", "jpg", "jpeg", "png", "gif", "zip", "webp"}
IGNORED_EXTENSIONS = []string{"mp4", "avi", "mpeg", "mpg", "mov", "wmv", "m4p", "swf", "mp2", "flv", "vob", "webm", "hdv", "3gp", "ogg", "mp3", "wav", "flac", "webp"}
)
func SkipFile(filename string, data []byte) bool {
if filepath.Ext(filename) == "" {
//no sepcified extension, check mimetype
if filetype.IsArchive(data[:256]) {
return true
}
}
func SkipFile(filename string) bool {
for _, ext := range IGNORED_EXTENSIONS {
if filepath.Ext(filename) == ext {
return true
+45
View File
@@ -0,0 +1,45 @@
package config
import (
"os"
"github.com/trufflesecurity/trufflehog/v3/pkg/custom_detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/custom_detectorspb"
"github.com/trufflesecurity/trufflehog/v3/pkg/protoyaml"
)
// Config holds user supplied configuration.
type Config struct {
Detectors []detectors.Detector
}
// Read parses a given filename into a Config.
func Read(filename string) (*Config, error) {
input, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
return NewYAML(input)
}
// NewYAML parses the given YAML data into a Config.
func NewYAML(input []byte) (*Config, error) {
// Parse the raw YAML into a structure.
var messages custom_detectorspb.CustomDetectors
if err := protoyaml.UnmarshalStrict(input, &messages); err != nil {
return nil, err
}
// Convert the structured YAML into detectors.
var detectors []detectors.Detector
for _, detectorConfig := range messages.Detectors {
detector, err := custom_detectors.NewWebhookCustomRegex(detectorConfig)
if err != nil {
return nil, err
}
detectors = append(detectors, detector)
}
return &Config{
Detectors: detectors,
}, nil
}
+39 -6
View File
@@ -2,6 +2,8 @@ package context
import (
"context"
"fmt"
"runtime/debug"
"time"
"github.com/go-logr/logr"
@@ -38,6 +40,7 @@ type logCtx struct {
// Embed context.Context to get all methods for free.
context.Context
log logr.Logger
err *error
}
// Logger returns a structured logger.
@@ -45,6 +48,13 @@ func (l logCtx) Logger() logr.Logger {
return l.log
}
func (l logCtx) Err() error {
if l.err != nil && *l.err != nil {
return *l.err
}
return l.Context.Err()
}
// Background returns context.Background with a default logger.
func Background() Context {
return logCtx{
@@ -64,30 +74,33 @@ func TODO() Context {
// WithCancel returns context.WithCancel with the log object propagated.
func WithCancel(parent Context) (Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(parent)
return logCtx{
lCtx := logCtx{
log: parent.Logger(),
Context: ctx,
}, cancel
}
return captureCancelCallstack(lCtx, cancel)
}
// WithDeadline returns context.WithDeadline with the log object propagated and
// the deadline added to the structured log values.
func WithDeadline(parent Context, d time.Time) (Context, context.CancelFunc) {
ctx, cancel := context.WithDeadline(parent, d)
return logCtx{
lCtx := logCtx{
log: parent.Logger().WithValues("deadline", d),
Context: ctx,
}, cancel
}
return captureCancelCallstack(lCtx, cancel)
}
// WithTimeout returns context.WithTimeout with the log object propagated and
// the timeout added to the structured log values.
func WithTimeout(parent Context, timeout time.Duration) (Context, context.CancelFunc) {
ctx, cancel := context.WithTimeout(parent, timeout)
return logCtx{
lCtx := logCtx{
log: parent.Logger().WithValues("timeout", timeout),
Context: ctx,
}, cancel
}
return captureCancelCallstack(lCtx, cancel)
}
// WithValue returns context.WithValue with the log object propagated and
@@ -136,3 +149,23 @@ func AddLogger(parent context.Context) Context {
func SetDefaultLogger(l logr.Logger) {
defaultLogger = l
}
// captureCancelCallstack is a helper function to capture the callstack where
// the cancel function was first called.
func captureCancelCallstack(ctx logCtx, f context.CancelFunc) (Context, context.CancelFunc) {
if ctx.err == nil {
var err error
ctx.err = &err
}
return ctx, func() {
// We must check Err() before calling f() since f() sets the error.
// If there's already an error, do nothing special.
if ctx.Err() != nil {
f()
return
}
f()
// Set the error with the stacktrace if the err pointer is non-nil.
*ctx.err = fmt.Errorf("%w (%s)", ctx.Err(), string(debug.Stack()))
}
}
+43
View File
@@ -169,3 +169,46 @@ func TestDiscardLogger(t *testing.T) {
ctx := Background()
ctx.Logger().Info("this shouldn't panic")
}
func TestErrCallstack(t *testing.T) {
c, cancel := WithCancel(Background())
ctx := c.(logCtx)
cancel()
select {
case <-ctx.Done():
assert.Contains(t, ctx.Err().Error(), "TestErrCallstack")
case <-time.After(1 * time.Second):
assert.Fail(t, "context should be done")
}
}
func TestErrCallstackTimeout(t *testing.T) {
ctx, cancel := WithTimeout(Background(), 10*time.Millisecond)
defer cancel()
select {
case <-ctx.Done():
// Deadline exceeded errors will not have a callstack from the cancel
// function.
assert.NotContains(t, ctx.Err().Error(), "TestErrCallstackTimeout")
case <-time.After(1 * time.Second):
assert.Fail(t, "context should be done")
}
}
func TestErrCallstackTimeoutCancel(t *testing.T) {
ctx, cancel := WithTimeout(Background(), 10*time.Millisecond)
var err error
select {
case <-ctx.Done():
err = ctx.Err()
case <-time.After(1 * time.Second):
assert.Fail(t, "context should be done")
}
// Calling cancel after deadline exceeded should not overwrite the original
// error.
cancel()
assert.Equal(t, err, ctx.Err())
}
+226
View File
@@ -0,0 +1,226 @@
package custom_detectors
import (
"bytes"
"context"
"encoding/json"
"net/http"
"regexp"
"strings"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/custom_detectorspb"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)
// The maximum number of matches from one chunk. This const is used when
// permutating each regex match to protect the scanner from doing too much work
// for poorly defined regexps.
const maxTotalMatches = 100
// customRegexWebhook is a CustomRegex with webhook validation that is
// guaranteed to be valid (assuming the data is not changed after
// initialization).
type customRegexWebhook struct {
*custom_detectorspb.CustomRegex
}
// Ensure the Scanner satisfies the interface at compile time.
var _ detectors.Detector = (*customRegexWebhook)(nil)
// NewWebhookCustomRegex initializes and validates a customRegexWebhook. An
// unexported type is intentionally returned here to ensure the values have
// been validated.
func NewWebhookCustomRegex(pb *custom_detectorspb.CustomRegex) (*customRegexWebhook, error) {
// TODO: Return all validation errors.
if err := ValidateKeywords(pb.Keywords); err != nil {
return nil, err
}
if err := ValidateRegex(pb.Regex); err != nil {
return nil, err
}
for _, verify := range pb.Verify {
if err := ValidateVerifyEndpoint(verify.Endpoint, verify.Unsafe); err != nil {
return nil, err
}
if err := ValidateVerifyHeaders(verify.Headers); err != nil {
return nil, err
}
}
// TODO: Copy only necessary data out of pb.
return &customRegexWebhook{pb}, nil
}
var httpClient = common.SaneHttpClient()
func (c *customRegexWebhook) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
dataStr := string(data)
regexMatches := make(map[string][][]string, len(c.GetRegex()))
// Find all submatches for each regex.
for name, regex := range c.GetRegex() {
regex, err := regexp.Compile(regex)
if err != nil {
// This will only happen if the regex is invalid.
return nil, err
}
regexMatches[name] = regex.FindAllStringSubmatch(dataStr, -1)
}
// Permutate each individual match.
// {
// "foo": [["match1"]]
// "bar": [["match2"], ["match3"]]
// }
// becomes
// [
// {"foo": ["match1"], "bar": ["match2"]},
// {"foo": ["match1"], "bar": ["match3"]},
// ]
matches := permutateMatches(regexMatches)
// Create result object and test for verification.
for _, match := range matches {
if common.IsDone(ctx) {
// TODO: Log we're possibly leaving out results.
return results, nil
}
var raw string
for _, values := range match {
// values[0] contains the entire regex match.
raw += values[0]
}
result := detectors.Result{
DetectorType: detectorspb.DetectorType_CustomRegex,
Raw: []byte(raw),
}
if isKnownFalsePositive(match) {
continue
}
if !verify {
results = append(results, result)
continue
}
// Verify via webhook.
jsonBody, err := json.Marshal(map[string]map[string][]string{
c.GetName(): match,
})
if err != nil {
continue
}
// Try each config until we successfully verify.
for _, verifyConfig := range c.GetVerify() {
if common.IsDone(ctx) {
// TODO: Log we're possibly leaving out results.
return results, nil
}
req, err := http.NewRequestWithContext(ctx, "POST", verifyConfig.GetEndpoint(), bytes.NewReader(jsonBody))
if err != nil {
continue
}
for _, header := range verifyConfig.GetHeaders() {
key, value, found := strings.Cut(header, ":")
if !found {
// Should be unreachable due to validation.
continue
}
req.Header.Add(key, strings.TrimLeft(value, "\t\n\v\f\r "))
}
res, err := httpClient.Do(req)
if err != nil {
continue
}
// TODO: Read response body.
res.Body.Close()
if res.StatusCode == http.StatusOK {
result.Verified = true
break
}
}
results = append(results, result)
}
return results, nil
}
func (c *customRegexWebhook) Keywords() []string {
return c.GetKeywords()
}
// productIndices produces a permutation of indices for each length. Example:
// productIndices(3, 2) -> [[0 0] [1 0] [2 0] [0 1] [1 1] [2 1]]. It returns
// a slice of length no larger than maxTotalMatches.
func productIndices(lengths ...int) [][]int {
count := 1
for _, l := range lengths {
count *= l
}
if count == 0 {
return nil
}
if count > maxTotalMatches {
count = maxTotalMatches
}
results := make([][]int, count)
for i := 0; i < count; i++ {
j := 1
result := make([]int, 0, len(lengths))
for _, l := range lengths {
result = append(result, (i/j)%l)
j *= l
}
results[i] = result
}
return results
}
// permutateMatches converts the list of all regex matches into all possible
// permutations selecting one from each named entry in the map. For example:
// {"foo": [matchA, matchB], "bar": [matchC]} becomes
//
// [{"foo": matchA, "bar": matchC}, {"foo": matchB, "bar": matchC}]
func permutateMatches(regexMatches map[string][][]string) []map[string][]string {
// Get a consistent order for names and their matching lengths.
// The lengths are used in calculating the permutation so order matters.
names := make([]string, 0, len(regexMatches))
lengths := make([]int, 0, len(regexMatches))
for key, value := range regexMatches {
names = append(names, key)
lengths = append(lengths, len(value))
}
// Permutate all the indices for each match. For example, if "foo" has
// [matchA, matchB] and "bar" has [matchC], we will get indices [0 0] [1 0].
permutationIndices := productIndices(lengths...)
// Build {"foo": matchA, "bar": matchC} and {"foo": matchB, "bar": matchC}
// from the indices.
var matches []map[string][]string
for _, permutation := range permutationIndices {
candidate := make(map[string][]string, len(permutationIndices))
for i, name := range names {
candidate[name] = regexMatches[name][permutation[i]]
}
matches = append(matches, candidate)
}
return matches
}
// This function will check false positives for common test words, but also it
// will make sure the key appears 'random' enough to be a real key.
func isKnownFalsePositive(match map[string][]string) bool {
for _, values := range match {
for _, value := range values {
if detectors.IsKnownFalsePositive(value, detectors.DefaultFalsePositives, true) {
return true
}
}
}
return false
}
@@ -0,0 +1,200 @@
package custom_detectors
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/custom_detectorspb"
"github.com/trufflesecurity/trufflehog/v3/pkg/protoyaml"
)
func TestCustomRegexTemplateParsing(t *testing.T) {
testCustomRegexTemplateYaml := `name: Internal bi tool
keywords:
- secret_v1_
- pat_v2_
regex:
id_pat_example: ([a-zA-Z0-9]{32})
secret_pat_example: ([a-zA-Z0-9]{32})
verify:
- endpoint: http://localhost:8000/{id_pat_example}
unsafe: true
headers:
- 'Authorization: Bearer {secret_pat_example.0}'
successRanges:
- 200-250
- '288'`
var got custom_detectorspb.CustomRegex
assert.NoError(t, protoyaml.UnmarshalStrict([]byte(testCustomRegexTemplateYaml), &got))
assert.Equal(t, "Internal bi tool", got.Name)
assert.Equal(t, []string{"secret_v1_", "pat_v2_"}, got.Keywords)
assert.Equal(t, map[string]string{
"id_pat_example": "([a-zA-Z0-9]{32})",
"secret_pat_example": "([a-zA-Z0-9]{32})",
}, got.Regex)
assert.Equal(t, 1, len(got.Verify))
assert.Equal(t, "http://localhost:8000/{id_pat_example}", got.Verify[0].Endpoint)
assert.Equal(t, true, got.Verify[0].Unsafe)
assert.Equal(t, []string{"Authorization: Bearer {secret_pat_example.0}"}, got.Verify[0].Headers)
assert.Equal(t, []string{"200-250", "288"}, got.Verify[0].SuccessRanges)
}
func TestCustomRegexWebhookParsing(t *testing.T) {
testCustomRegexWebhookYaml := `name: Internal bi tool
keywords:
- secret_v1_
- pat_v2_
regex:
id_pat_example: ([a-zA-Z0-9]{32})
secret_pat_example: ([a-zA-Z0-9]{32})
verify:
- endpoint: http://localhost:8000/
unsafe: true
headers:
- 'Authorization: Bearer token'`
var got custom_detectorspb.CustomRegex
assert.NoError(t, protoyaml.UnmarshalStrict([]byte(testCustomRegexWebhookYaml), &got))
assert.Equal(t, "Internal bi tool", got.Name)
assert.Equal(t, []string{"secret_v1_", "pat_v2_"}, got.Keywords)
assert.Equal(t, map[string]string{
"id_pat_example": "([a-zA-Z0-9]{32})",
"secret_pat_example": "([a-zA-Z0-9]{32})",
}, got.Regex)
assert.Equal(t, 1, len(got.Verify))
assert.Equal(t, "http://localhost:8000/", got.Verify[0].Endpoint)
assert.Equal(t, true, got.Verify[0].Unsafe)
assert.Equal(t, []string{"Authorization: Bearer token"}, got.Verify[0].Headers)
}
// TestCustomDetectorsParsing tests the full `detectors` configuration.
func TestCustomDetectorsParsing(t *testing.T) {
// TODO: Support both template and webhook.
testYamlConfig := `detectors:
- name: Internal bi tool
keywords:
- secret_v1_
- pat_v2_
regex:
id_pat_example: ([a-zA-Z0-9]{32})
secret_pat_example: ([a-zA-Z0-9]{32})
verify:
- endpoint: http://localhost:8000/
unsafe: true
headers:
- 'Authorization: Bearer token'`
var messages custom_detectorspb.CustomDetectors
assert.NoError(t, protoyaml.UnmarshalStrict([]byte(testYamlConfig), &messages))
assert.Equal(t, 1, len(messages.Detectors))
got := messages.Detectors[0]
assert.Equal(t, "Internal bi tool", got.Name)
assert.Equal(t, []string{"secret_v1_", "pat_v2_"}, got.Keywords)
assert.Equal(t, map[string]string{
"id_pat_example": "([a-zA-Z0-9]{32})",
"secret_pat_example": "([a-zA-Z0-9]{32})",
}, got.Regex)
assert.Equal(t, 1, len(got.Verify))
assert.Equal(t, "http://localhost:8000/", got.Verify[0].Endpoint)
assert.Equal(t, true, got.Verify[0].Unsafe)
assert.Equal(t, []string{"Authorization: Bearer token"}, got.Verify[0].Headers)
}
func TestFromData_InvalidRegEx(t *testing.T) {
c := &customRegexWebhook{
&custom_detectorspb.CustomRegex{
Name: "Internal bi tool",
Keywords: []string{"secret_v1_", "pat_v2_"},
Regex: map[string]string{
"test": "!!?(?:?)[a-zA-Z0-9]{32}", // invalid regex
},
},
}
_, err := c.FromData(context.Background(), false, []byte("test"))
assert.Error(t, err)
}
func TestProductIndices(t *testing.T) {
tests := []struct {
name string
input []int
want [][]int
}{
{
name: "zero",
input: []int{3, 0},
want: nil,
},
{
name: "one input",
input: []int{3},
want: [][]int{{0}, {1}, {2}},
},
{
name: "two inputs",
input: []int{3, 2},
want: [][]int{
{0, 0}, {1, 0}, {2, 0},
{0, 1}, {1, 1}, {2, 1},
},
},
{
name: "three inputs",
input: []int{3, 2, 3},
want: [][]int{
{0, 0, 0}, {1, 0, 0}, {2, 0, 0},
{0, 1, 0}, {1, 1, 0}, {2, 1, 0},
{0, 0, 1}, {1, 0, 1}, {2, 0, 1},
{0, 1, 1}, {1, 1, 1}, {2, 1, 1},
{0, 0, 2}, {1, 0, 2}, {2, 0, 2},
{0, 1, 2}, {1, 1, 2}, {2, 1, 2},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := productIndices(tt.input...)
assert.Equal(t, tt.want, got)
})
}
}
func TestProductIndicesMax(t *testing.T) {
got := productIndices(2, 3, 4, 5, 6)
assert.GreaterOrEqual(t, 2*3*4*5*6, maxTotalMatches)
assert.Equal(t, maxTotalMatches, len(got))
}
func TestPermutateMatches(t *testing.T) {
tests := []struct {
name string
input map[string][][]string
want []map[string][]string
}{
{
name: "two matches",
input: map[string][][]string{"foo": {{"matchA"}, {"matchB"}}, "bar": {{"matchC"}}},
want: []map[string][]string{
{"foo": {"matchA"}, "bar": {"matchC"}},
{"foo": {"matchB"}, "bar": {"matchC"}},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := permutateMatches(tt.input)
assert.Equal(t, tt.want, got)
})
}
}
func BenchmarkProductIndices(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = productIndices(3, 2, 6)
}
}
+44
View File
@@ -0,0 +1,44 @@
package custom_detectors
import (
"regexp"
"strconv"
"strings"
)
// nameGroupRegex matches `{ name . group }` ignoring any whitespace.
var nameGroupRegex = regexp.MustCompile(`{\s*([a-zA-Z0-9-_]+)\s*(\.\s*[0-9]*)?\s*}`)
// RegexVarString is a string with embedded {name.group} variables. A name may
// only contain alphanumeric, hyphen, and underscore characters. Group is
// optional but if provided it must be a non-negative integer. If the group is
// omitted it defaults to 0.
type RegexVarString struct {
original string
// map from name to group
variables map[string]int
}
func NewRegexVarString(original string) RegexVarString {
variables := make(map[string]int)
matches := nameGroupRegex.FindAllStringSubmatch(original, -1)
for _, match := range matches {
name, group := match[1], 0
// The second match will start with a period followed by any number
// of whitespace.
if len(match[2]) > 1 {
g, err := strconv.Atoi(strings.TrimSpace(match[2][1:]))
if err != nil {
continue
}
group = g
}
variables[name] = group
}
return RegexVarString{
original: original,
variables: variables,
}
}
@@ -0,0 +1,76 @@
package custom_detectors
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestVarString(t *testing.T) {
tests := []struct {
name string
input string
wantVars map[string]int
}{
{
name: "empty",
input: "{}",
wantVars: map[string]int{},
},
{
name: "no subgroup",
input: "{hello}",
wantVars: map[string]int{
"hello": 0,
},
},
{
name: "with subgroup",
input: "{hello.123}",
wantVars: map[string]int{
"hello": 123,
},
},
{
name: "subgroup with spaces",
input: "{\thell0 . 123 }",
wantVars: map[string]int{
"hell0": 123,
},
},
{
name: "multiple groups",
input: "foo {bar} {bazz.buzz} {buzz.2}",
wantVars: map[string]int{
"bar": 0,
"buzz": 2,
},
},
{
name: "nested groups",
input: "{foo {bar}}",
wantVars: map[string]int{
"bar": 0,
},
},
{
name: "decimal without number",
input: "{foo.}",
wantVars: map[string]int{
"foo": 0,
},
},
{
name: "negative number",
input: "{foo.-1}",
wantVars: map[string]int{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NewRegexVarString(tt.input)
assert.Equal(t, tt.input, got.original)
assert.Equal(t, tt.wantVars, got.variables)
})
}
}
+109
View File
@@ -0,0 +1,109 @@
package custom_detectors
import (
"fmt"
"regexp"
"strconv"
"strings"
)
func ValidateKeywords(keywords []string) error {
if len(keywords) == 0 {
return fmt.Errorf("no keywords")
}
for _, keyword := range keywords {
if len(keyword) == 0 {
return fmt.Errorf("empty keyword")
}
}
return nil
}
func ValidateRegex(regex map[string]string) error {
if len(regex) == 0 {
return fmt.Errorf("no regex")
}
for name, reg := range regex {
if _, err := regexp.Compile(reg); err != nil {
return fmt.Errorf("regex '%s': %w", name, err)
}
}
return nil
}
func ValidateVerifyEndpoint(endpoint string, unsafe bool) error {
if len(endpoint) == 0 {
return fmt.Errorf("no endpoint")
}
if strings.HasPrefix(endpoint, "http://") && !unsafe {
return fmt.Errorf("http endpoint must have unsafe=true")
}
return nil
}
func ValidateVerifyHeaders(headers []string) error {
for _, header := range headers {
if !strings.Contains(header, ":") {
return fmt.Errorf("header %q must contain a colon", header)
}
}
return nil
}
func ValidateVerifyRanges(ranges []string) error {
const httpLowerRange = 100
const httpUpperRange = 599
for _, successRange := range ranges {
if !strings.Contains(successRange, "-") {
httpCode, err := strconv.Atoi(successRange)
if err != nil {
return fmt.Errorf("unable to convert http code to int %q", successRange)
}
if httpCode < httpLowerRange || httpCode > httpUpperRange {
return fmt.Errorf("invalid http status code %q", successRange)
}
continue
}
httpRange := strings.Split(successRange, "-")
if len(httpRange) != 2 {
return fmt.Errorf("invalid range format %q", successRange)
}
lowerBound, err := strconv.Atoi(httpRange[0])
if err != nil {
return fmt.Errorf("unable to convert lower bound to int %q", successRange)
}
upperBound, err := strconv.Atoi(httpRange[1])
if err != nil {
return fmt.Errorf("unable to convert upper bound to int %q", successRange)
}
if lowerBound > upperBound {
return fmt.Errorf("lower bound greater than upper bound on range %q", successRange)
}
if lowerBound < httpLowerRange || upperBound > httpUpperRange {
return fmt.Errorf("invalid http status code range %q", successRange)
}
}
return nil
}
func ValidateRegexVars(regex map[string]string, body ...string) error {
for _, b := range body {
matches := NewRegexVarString(b).variables
for match := range matches {
if _, ok := regex[match]; !ok {
return fmt.Errorf("body %q contains an unknown variable", b)
}
}
}
return nil
}
+234
View File
@@ -0,0 +1,234 @@
package custom_detectors
import "testing"
func TestCustomDetectorsKeywordValidation(t *testing.T) {
tests := []struct {
name string
input []string
wantErr bool
}{
{
name: "Test empty list of keywords",
input: []string{},
wantErr: true,
},
{
name: "Test empty keyword",
input: []string{""},
wantErr: true,
},
{
name: "Test valid keywords",
input: []string{"hello", "world"},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateKeywords(tt.input)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateKeywords() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestCustomDetectorsRegexValidation(t *testing.T) {
tests := []struct {
name string
input map[string]string
wantErr bool
}{
{
name: "Test list of keywords",
input: map[string]string{
"id_pat_example": "([a-zA-Z0-9]{32})",
},
wantErr: false,
},
{
name: "Test empty list of keywords",
input: map[string]string{},
wantErr: true,
},
{
name: "Test invalid regex",
input: map[string]string{
"test": "!!?(?:?)[a-zA-Z0-9]{32}",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateRegex(tt.input)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateRegex() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestCustomDetectorsVerifyEndpointValidation(t *testing.T) {
tests := []struct {
name string
endpoint string
unsafe bool
wantErr bool
}{
{
name: "Test http endpoint with unsafe flag",
endpoint: "http://localhost:8000/{id_pat_example}",
unsafe: true,
wantErr: false,
},
{
name: "Test http endpoint without unsafe flag",
endpoint: "http://localhost:8000/{id_pat_example}",
unsafe: false,
wantErr: true,
},
{
name: "Test https endpoint with unsafe flag",
endpoint: "https://localhost:8000/{id_pat_example}",
unsafe: true,
wantErr: false,
},
{
name: "Test https endpoint without unsafe flag",
endpoint: "https://localhost:8000/{id_pat_example}",
unsafe: false,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateVerifyEndpoint(tt.endpoint, tt.unsafe)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateVerifyEndpoint() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestCustomDetectorsVerifyHeadersValidation(t *testing.T) {
tests := []struct {
name string
headers []string
wantErr bool
}{
{
name: "Test single header",
headers: []string{"Authorization: Bearer {secret_pat_example.0}"},
wantErr: false,
},
{
name: "Test invalid header",
headers: []string{"Hello world"},
wantErr: true,
},
{
name: "Test ugly header",
headers: []string{"Hello:::::::world::hi:"},
wantErr: false,
},
{
name: "Test empty header",
headers: []string{},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateVerifyHeaders(tt.headers)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateVerifyHeaders() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestCustomDetectorsVerifyRangeValidation(t *testing.T) {
tests := []struct {
name string
ranges []string
wantErr bool
}{
{
name: "Test multiple mixed ranges",
ranges: []string{"200", "300-350"},
wantErr: false,
},
{
name: "Test invalid non-number range",
ranges: []string{"hi"},
wantErr: true,
},
{
name: "Test invalid lower to upper range",
ranges: []string{"200-100"},
wantErr: true,
},
{
name: "Test invalid http range",
ranges: []string{"400-1000"},
wantErr: true,
},
{
name: "Test multiple ranges with invalid inputs",
ranges: []string{"322", "hello-world", "100-200"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateVerifyRanges(tt.ranges)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateVerifyRanges() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestCustomDetectorsVerifyRegexVarsValidation(t *testing.T) {
tests := []struct {
name string
regex map[string]string
body string
wantErr bool
}{
{
name: "Regex defined but not used in body",
regex: map[string]string{"id": "[0-9]{1,10}", "id_pat_example": "([a-zA-Z0-9]{32})"},
body: "hello world",
wantErr: false,
},
{
name: "Regex defined and is used in body",
regex: map[string]string{"id": "[0-9]{1,10}", "id_pat_example": "([a-zA-Z0-9]{32})"},
body: "hello world {id}",
wantErr: false,
},
{
name: "Regex var in body but not defined",
regex: map[string]string{"id": "[0-9]{1,10}", "id_pat_example": "([a-zA-Z0-9]{32})"},
body: "hello world {hello}",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateRegexVars(tt.regex, tt.body)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateRegexVars() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ import (
func DefaultDecoders() []Decoder {
return []Decoder{
&Plain{},
&UTF8{},
&Base64{},
}
}
-14
View File
@@ -1,14 +0,0 @@
package decoders
import (
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
)
// Ensure the Decoder satisfies the interface at compile time
var _ Decoder = (*Plain)(nil)
type Plain struct{}
func (d *Plain) FromChunk(chunk *sources.Chunk) *sources.Chunk {
return chunk
}
+44
View File
@@ -0,0 +1,44 @@
package decoders
import (
"bytes"
"unicode/utf8"
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
)
type UTF8 struct{}
func (d *UTF8) FromChunk(chunk *sources.Chunk) *sources.Chunk {
if chunk == nil || len(chunk.Data) == 0 {
return nil
}
if !utf8.Valid(chunk.Data) {
chunk.Data = extractSubstrings(chunk.Data)
return chunk
}
return chunk
}
// extractSubstrings performs similarly to the strings binutil,
// extacting contigous portions of printable characters that we care
// about from some bytes
func extractSubstrings(b []byte) []byte {
fields := bytes.FieldsFunc(b, func(r rune) bool {
// https://www.rapidtables.com/code/text/ascii-table.html
// split on anything that is not ascii space through tilde
return !(r > 31 && r < 127)
})
keep := [][]byte{}
for _, field := range fields {
// Remove fields shorter than 6 characters.
if bts := bytes.TrimSpace(field); len(bts) > 5 {
keep = append(keep, bts)
}
}
return bytes.Join(keep, []byte("\n"))
}
+87
View File
@@ -0,0 +1,87 @@
package decoders
import (
"testing"
"github.com/kylelemons/godebug/pretty"
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
)
func TestUTF8_FromChunk(t *testing.T) {
type args struct {
chunk *sources.Chunk
}
tests := []struct {
name string
d *UTF8
args args
want *sources.Chunk
wantErr bool
}{
{
name: "successful UTF8 decode",
d: &UTF8{},
args: args{
chunk: &sources.Chunk{Data: []byte("plain 'ol chunk that should decode successfully")},
},
want: &sources.Chunk{Data: []byte("plain 'ol chunk that should decode successfully")},
wantErr: false,
},
{
name: "successful binary decode",
d: &UTF8{},
args: args{
chunk: &sources.Chunk{Data: []byte("\xf0\x28\x8c\x28 not-entirely utf8 chunk that should decode successfully")},
},
want: &sources.Chunk{Data: []byte("( not-entirely utf8 chunk that should decode successfully")},
wantErr: false,
},
{
name: "unsuccessful decode",
d: &UTF8{},
args: args{
chunk: nil,
},
want: nil,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &UTF8{}
got := d.FromChunk(tt.args.chunk)
if got != nil && tt.want != nil {
if diff := pretty.Compare(string(got.Data), string(tt.want.Data)); diff != "" {
t.Errorf("%s: Plain.FromChunk() diff: (-got +want)\n%s", tt.name, diff)
}
} else {
if diff := pretty.Compare(got, tt.want); diff != "" {
t.Errorf("%s: Plain.FromChunk() diff: (-got +want)\n%s", tt.name, diff)
}
}
})
}
}
var testBytes = []byte(`some words with random spaces and
newlines with
arbitrary length
of
hey
the lines themselves.
and
short
words
that
go
away.`)
func Benchmark_extractSubstrings(b *testing.B) {
for i := 0; i < b.N; i++ {
extractSubstrings(testBytes)
}
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -81,5 +81,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -68,5 +68,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -68,5 +68,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -80,5 +80,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -78,5 +78,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -81,5 +81,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -81,5 +81,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -71,5 +71,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
@@ -85,5 +85,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
return detectors.CleanResults(results), nil
return results, nil
}
@@ -68,5 +68,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -71,5 +71,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
@@ -79,5 +79,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -71,5 +71,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -74,5 +74,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -80,5 +80,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -72,5 +72,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
@@ -78,5 +78,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -128,5 +128,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -69,5 +69,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -75,5 +75,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -86,5 +86,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
@@ -79,5 +79,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -80,5 +80,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -69,5 +69,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -80,7 +80,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
type Response struct {
+1 -1
View File
@@ -82,5 +82,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -78,5 +78,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -81,5 +81,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -68,5 +68,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+6 -6
View File
@@ -2,12 +2,12 @@ package apilayer
import (
"context"
"net/http"
"regexp"
"strings"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
"net/http"
"regexp"
"strings"
)
type Scanner struct{}
@@ -18,7 +18,7 @@ var _ detectors.Detector = (*Scanner)(nil)
var (
client = common.SaneHttpClient()
//Make sure that your group is surrounded in boundry characters such as below to reduce false positives
// Make sure that your group is surrounded in boundry characters such as below to reduce false positives
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apilayer"}) + `\b([a-zA-Z0-9]{32})\b`)
)
@@ -57,7 +57,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
} else {
//This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key
// This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key
if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
continue
}
@@ -68,5 +68,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -82,5 +82,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -69,5 +69,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -90,5 +90,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -73,5 +73,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -74,5 +74,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -89,5 +89,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -81,5 +81,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -80,5 +80,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
@@ -69,5 +69,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -79,5 +79,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
@@ -88,5 +88,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -114,5 +114,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -80,5 +80,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -79,5 +79,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -69,5 +69,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
@@ -21,7 +21,7 @@ var (
client = common.SaneHttpClient()
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
//The number prefix increments for every Personal Access Token created.
// The number prefix increments for every Personal Access Token created.
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"avaza"}) + `\b([0-9]+-[0-9a-f]{40})\b`)
)
@@ -71,5 +71,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -72,5 +72,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+36 -5
View File
@@ -17,10 +17,35 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)
type Scanner struct{}
type scanner struct {
skipIDs map[string]struct{}
}
// Ensure the Scanner satisfies the interface at compile time.
var _ detectors.Detector = (*Scanner)(nil)
func New(opts ...func(*scanner)) *scanner {
scanner := &scanner{
skipIDs: map[string]struct{}{},
}
for _, opt := range opts {
opt(scanner)
}
return scanner
}
func WithSkipIDs(skipIDs []string) func(*scanner) {
return func(s *scanner) {
ids := map[string]struct{}{}
for _, id := range skipIDs {
ids[id] = struct{}{}
}
s.skipIDs = ids
}
}
// Ensure the scanner satisfies the interface at compile time.
var _ detectors.Detector = (*scanner)(nil)
var (
client = common.SaneHttpClient()
@@ -37,7 +62,7 @@ var (
// Keywords are used for efficiently pre-filtering chunks.
// Use identifiers in the secret preferably, or the provider name.
func (s Scanner) Keywords() []string {
func (s scanner) Keywords() []string {
return []string{
"AKIA",
"ABIA",
@@ -60,7 +85,7 @@ func GetHMAC(key []byte, data []byte) []byte {
}
// FromData will find and optionally verify AWS secrets in a given set of bytes.
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
func (s scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
dataStr := string(data)
idMatches := idPat.FindAllStringSubmatch(dataStr, -1)
@@ -72,6 +97,12 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
resIDMatch := strings.TrimSpace(idMatch[1])
if s.skipIDs != nil {
if _, ok := s.skipIDs[resIDMatch]; ok {
continue
}
}
for _, secretMatch := range secretMatches {
if len(secretMatch) != 2 {
continue
+24 -10
View File
@@ -39,14 +39,14 @@ func TestAWS_FromChunk(t *testing.T) {
}
tests := []struct {
name string
s Scanner
s scanner
args args
want []detectors.Result
wantErr bool
}{
{
name: "found, verified",
s: Scanner{},
s: scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s", secret, id)),
@@ -68,7 +68,7 @@ func TestAWS_FromChunk(t *testing.T) {
},
{
name: "found, unverified",
s: Scanner{},
s: scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation
@@ -86,7 +86,7 @@ func TestAWS_FromChunk(t *testing.T) {
},
{
name: "not found",
s: Scanner{},
s: scanner{},
args: args{
ctx: context.Background(),
data: []byte("You cannot find the secret within"),
@@ -97,7 +97,7 @@ func TestAWS_FromChunk(t *testing.T) {
},
{
name: "found two, one included for every ID found",
s: Scanner{},
s: scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("The verified ID is %s with a secret of %s, but the unverified ID is %s and this is the secret %s", id, secret, inactiveID, inactiveSecret)),
@@ -124,7 +124,7 @@ func TestAWS_FromChunk(t *testing.T) {
},
{
name: "not found, because unverified secret was a hash",
s: Scanner{},
s: scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s but not valid", hash, id)), // The secret would satisfy the regex but be filtered out after not passing validation.
@@ -135,7 +135,7 @@ func TestAWS_FromChunk(t *testing.T) {
},
{
name: "found two, returned both because the active secret for one paired with the inactive ID, despite the hash",
s: Scanner{},
s: scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("The verified ID is %s with a secret of %s, but the unverified ID is %s and the secret is this hash %s", id, secret, inactiveID, hash)),
@@ -162,7 +162,7 @@ func TestAWS_FromChunk(t *testing.T) {
},
{
name: "found, unverified, with leading +",
s: Scanner{},
s: scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s but not valid", "+HaNv9cTwheDKGJaws/+BMF2GgybQgBWdhcOOdfF", id)), // the secret would satisfy the regex but not pass validation
@@ -177,10 +177,24 @@ func TestAWS_FromChunk(t *testing.T) {
},
wantErr: false,
},
{
name: "skipped",
s: scanner{
skipIDs: map[string]struct{}{
"AKIAWARWQKZNHMZBLY4I": {},
},
},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s but not valid", "+HaNv9cTwheDKGJaws/+BMF2GgybQgBWdhcOOdfF", id)), // the secret would satisfy the regex but not pass validation
verify: true,
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := Scanner{}
s := tt.s
got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("AWS.FromData() error = %v, wantErr %v", err, tt.wantErr)
@@ -201,7 +215,7 @@ func TestAWS_FromChunk(t *testing.T) {
func BenchmarkFromData(benchmark *testing.B) {
ctx := context.Background()
s := Scanner{}
s := scanner{}
for name, data := range detectors.MustGetBenchmarkData() {
benchmark.Run(name, func(b *testing.B) {
for n := 0; n < b.N; n++ {
+1 -1
View File
@@ -69,5 +69,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -79,5 +79,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -83,5 +83,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -70,5 +70,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -77,5 +77,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -69,5 +69,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -75,5 +75,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+4 -4
View File
@@ -2,11 +2,11 @@ package besnappy
import (
"context"
b64 "encoding/base64"
"fmt"
"net/http"
"regexp"
"strings"
b64 "encoding/base64"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
@@ -21,7 +21,7 @@ var _ detectors.Detector = (*Scanner)(nil)
var (
client = common.SaneHttpClient()
//Make sure that your group is surrounded in boundry characters such as below to reduce false positives
// Make sure that your group is surrounded in boundry characters such as below to reduce false positives
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"besnappy"}) + `\b([a-f0-9]{64})\b`)
)
@@ -61,7 +61,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
} else {
//This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key
// This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key
if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
continue
}
@@ -72,5 +72,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -75,5 +75,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -80,5 +80,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
}
return detectors.CleanResults(results), nil
return results, nil
}
+1 -1
View File
@@ -74,5 +74,5 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
results = append(results, s1)
}
return detectors.CleanResults(results), nil
return results, nil
}

Some files were not shown because too many files have changed in this diff Show More