Skip to content

Commit

Permalink
refactor(cli): add --results flag
Browse files Browse the repository at this point in the history
  • Loading branch information
rgmz authored and Richard Gomez committed Feb 9, 2024
1 parent 6c09991 commit 9b21612
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 64 deletions.
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ This required Cosign binary to be installed prior to running installation script
Command:

```bash
trufflehog git https://github.com/trufflesecurity/test_keys --only-verified
trufflehog git https://github.com/trufflesecurity/test_keys --results=verified
```

Expected output:
Expand All @@ -141,15 +141,15 @@ Timestamp: 2022-06-16 10:17:40 -0700 PDT
## 2: Scan a GitHub Org for only verified secrets

```bash
trufflehog github --org=trufflesecurity --only-verified
trufflehog github --org=trufflesecurity --results=verified
```

## 3: Scan a GitHub Repo for only verified keys and get JSON output

Command:

```bash
trufflehog git https://github.com/trufflesecurity/test_keys --only-verified --json
trufflehog git https://github.com/trufflesecurity/test_keys --results=verified --json
```

Expected output:
Expand All @@ -168,7 +168,7 @@ trufflehog github --repo=https://github.com/trufflesecurity/test_keys --issue-co
## 5: Scan an S3 bucket for verified keys

```bash
trufflehog s3 --bucket=<bucket name> --only-verified
trufflehog s3 --bucket=<bucket name> --results=verified
```

## 6: Scan S3 buckets using IAM Roles
Expand All @@ -192,23 +192,23 @@ trufflehog filesystem path/to/file1.txt path/to/file2.txt path/to/dir
## 9: Scan GCS buckets for verified secrets.

```bash
trufflehog gcs --project-id=<project-ID> --cloud-environment --only-verified
trufflehog gcs --project-id=<project-ID> --cloud-environment --results=verified
```

## 10: Scan a Docker image for verified secrets.

Use the `--image` flag multiple times to scan multiple images.

```bash
trufflehog docker --image trufflesecurity/secrets --only-verified
trufflehog docker --image trufflesecurity/secrets --results=verified
```

## 11: Scan in CI

Set the `--since-commit` flag to your default branch that people merge into (ex: "main"). Set the `--branch` flag to your PR's branch name (ex: "feature-1"). Depending on the CI/CD platform you use, this value can be pulled in dynamically (ex: [CIRCLE_BRANCH in Circle CI](https://circleci.com/docs/variables/) and [TRAVIS_PULL_REQUEST_BRANCH in Travis CI](https://docs.travis-ci.com/user/environment-variables/)). If the repo is cloned and the target branch is already checked out during the CI/CD workflow, then `--branch HEAD` should be sufficient. The `--fail` flag will return an 183 error code if valid credentials are found.

```bash
trufflehog git file://. --since-commit main --branch feature-1 --only-verified --fail
trufflehog git file://. --since-commit main --branch feature-1 --results=verified --fail
```

# :question: FAQ
Expand Down Expand Up @@ -269,7 +269,7 @@ Flags:
--github-actions Output in GitHub Actions format.
--concurrency=8 Number of concurrent workers.
--no-verification Don't verify the results.
--only-verified Only output verified results.
--results=verified... ... Specifies which type(s) of results to output: verified, unknown, and unverified. This flag can be repeated.
--filter-unverified Only output first unverified result per chunk per detector if there are more than one results.
--filter-entropy=FILTER-ENTROPY
Filter unverified results with Shannon entropy. Start with 3.0.
Expand Down Expand Up @@ -363,7 +363,7 @@ jobs:
- name: Secret Scanning
uses: trufflesecurity/trufflehog@main
with:
extra_args: --only-verified
extra_args: --results=verified
```

In the example config above, we're scanning for live secrets in all PRs and Pushes to `main`. Only code changes in the referenced commits are scanned. If you'd like to scan an entire branch, please see the "Advanced Usage" section below.
Expand Down Expand Up @@ -391,7 +391,7 @@ If you're incorporating TruffleHog into a standalone workflow and aren't running
fetch-depth: ${{env.depth}}
- uses: trufflesecurity/trufflehog@main
with:
extra_args: --only-verified
extra_args: --results=verified
...
```

Expand All @@ -410,7 +410,7 @@ Depending on the event type (push or PR), we calculate the number of commits pre
# Scan commits until here (usually dev branch).
head: # optional
# Extra args to be passed to the trufflehog cli.
extra_args: --debug --only-verified
extra_args: --debug --results=verified
```

If you'd like to specify specific `base` and `head` refs, you can use the `base` argument (`--since-commit` flag in TruffleHog CLI) and the `head` argument (`--branch` flag in the TruffleHog CLI). We only recommend using these arguments for very specific use cases, where the default behavior does not work.
Expand All @@ -422,7 +422,7 @@ If you'd like to specify specific `base` and `head` refs, you can use the `base`
with:
base: ""
head: ${{ github.ref_name }}
extra_args: --only-verified
extra_args: --results=verified
```

## Pre-commit Hook
Expand All @@ -443,9 +443,9 @@ repos:
- id: trufflehog
name: TruffleHog
description: Detect secrets in your data.
entry: bash -c 'trufflehog git file://. --since-commit HEAD --only-verified --fail'
entry: bash -c 'trufflehog git file://. --since-commit HEAD --results=verified --fail'
# For running trufflehog in docker, use the following entry instead:
# entry: bash -c 'docker run --rm -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --since-commit HEAD --only-verified --fail'
# entry: bash -c 'docker run --rm -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --since-commit HEAD --results=verified --fail'
language: system
stages: ["commit", "push"]
```
Expand Down Expand Up @@ -485,7 +485,7 @@ detectors:


```
$ trufflehog filesystem /tmp --config config.yaml --only-verified
$ trufflehog filesystem /tmp --config config.yaml --results=verified
🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷
Found verified result 🐷🔑
Expand Down
38 changes: 17 additions & 21 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,19 @@ 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()
profile = cli.Flag("profile", "Enables profiling and sets a pprof and fgprof server on :18066.").Bool()
localDev = cli.Flag("local-dev", "Hidden feature to disable overseer for local dev.").Hidden().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()
gitHubActionsFormat = cli.Flag("github-actions", "Output in GitHub Actions format.").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()
logVerificationErrorsIsSet bool
logVerificationErrors = cli.Flag("log-verification-errors", "Output verification errors").IsSetByUser(&logVerificationErrorsIsSet).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()
profile = cli.Flag("profile", "Enables profiling and sets a pprof and fgprof server on :18066.").Bool()
localDev = cli.Flag("local-dev", "Hidden feature to disable overseer for local dev.").Hidden().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()
gitHubActionsFormat = cli.Flag("github-actions", "Output in GitHub Actions format.").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.").Hidden().Bool()
results = cli.Flag("results", "Specifies which type(s) of results to output: verified, unknown, unverified. This flag can be repeated. Defaults to all types.").Enums("verified", "unknown", "unverified")

allowVerificationOverlap = cli.Flag("allow-verification-overlap", "Allow verification of similar credentials across detectors").Bool()
filterUnverified = cli.Flag("filter-unverified", "Only output first unverified result per chunk per detector if there are more than one results.").Bool()
Expand Down Expand Up @@ -401,11 +400,9 @@ func run(state overseer.State) {
fmt.Fprintf(os.Stderr, "🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷\n\n")
}

// If |logVerificationErrors| was not set by the user,
// default to false if --only-verified is specified.
if !logVerificationErrorsIsSet {
value := !*onlyVerified
logVerificationErrors = &value
if *onlyVerified {
r := []string{"verified"}
results = &r
}

e, err := engine.Start(ctx,
Expand All @@ -418,8 +415,7 @@ func run(state overseer.State) {
engine.WithFilterDetectors(excludeFilter),
engine.WithFilterDetectors(endpointCustomizer),
engine.WithFilterUnverified(*filterUnverified),
engine.WithOnlyVerified(*onlyVerified),
engine.WithLogVerificationErrors(*logVerificationErrors),
engine.WithResults(*results),
engine.WithPrintAvgDetectorTime(*printAvgDetectorTime),
engine.WithPrinter(printer),
engine.WithFilterEntropy(*filterEntropy),
Expand Down
60 changes: 39 additions & 21 deletions pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/adrg/strutil"
"github.com/adrg/strutil/metrics"
lru "github.com/hashicorp/golang-lru"
"golang.org/x/exp/slices"
"google.golang.org/protobuf/proto"

"github.com/trufflesecurity/trufflehog/v3/pkg/cleantemp"
Expand Down Expand Up @@ -53,7 +54,7 @@ type runtimeMetrics struct {
// Printer is used to format found results and output them to the user. Ex JSON, plain text, etc.
// Please note printer implementations SHOULD BE thread safe.
type Printer interface {
Print(ctx context.Context, r *detectors.ResultWithMetadata, logErrors *bool) error
Print(ctx context.Context, r *detectors.ResultWithMetadata) error
}

type Engine struct {
Expand All @@ -66,11 +67,12 @@ type Engine struct {
// only the first one will be kept.
filterUnverified bool
// entropyFilter is used to filter out unverified results using Shannon entropy.
filterEntropy *float64
onlyVerified bool
logVerificationErrors bool
verificationOverlap bool
printAvgDetectorTime bool
filterEntropy *float64
verifiedResults bool
unverifiedResults bool
unknownResults bool
verificationOverlap bool
printAvgDetectorTime bool

// ahoCorasickHandler manages the Aho-Corasick trie and related keyword lookups.
ahoCorasickCore *ahocorasick.AhoCorasickCore
Expand Down Expand Up @@ -157,20 +159,26 @@ func WithFilterEntropy(entropy float64) Option {
}
}

// WithOnlyVerified sets the onlyVerified flag on the engine. If set to true,
// the engine will only print verified results.
func WithOnlyVerified(onlyVerified bool) Option {
return func(e *Engine) {
e.onlyVerified = onlyVerified
}
}

// WithLogVerificationErrors sets the |logVerificationErrors| flag on the engine.
// WithResults sets the |logVerificationErrors| flag on the engine.
// If set to true, the engine will print results with verification errors,
// even if the result is unverified and |onlyVerified| is true.
func WithLogVerificationErrors(logVerificationErrors bool) Option {
func WithResults(results []string) Option {
return func(e *Engine) {
e.logVerificationErrors = logVerificationErrors
if len(results) > 0 {
if slices.Contains(results, "verified") {
e.verifiedResults = true
}
if slices.Contains(results, "unknown") {
e.unknownResults = true
}
if slices.Contains(results, "unverified") {
e.unverifiedResults = true
}
} else {
e.verifiedResults = true
e.unknownResults = true
e.unverifiedResults = true
}
}
}

Expand Down Expand Up @@ -820,11 +828,21 @@ func (e *Engine) processResult(ctx context.Context, data detectableChunk, res de

func (e *Engine) notifyResults(ctx context.Context) {
for r := range e.ResultsChan() {
if !r.Verified && e.onlyVerified {
// Skip unverified errors, unless they have a verification error and |logVerificationErrors| is true.
if !(e.logVerificationErrors && r.VerificationError() != nil) {
// Filter unwanted results, based on `--results`.
if !r.Verified {
if r.VerificationError() != nil {
if !e.unknownResults {
// Skip results with verification errors.
continue
}
} else if !e.unverifiedResults {
// Skip unverified results.
continue
}
} else if !e.verifiedResults {
// Skip verified results.
// TODO: Is this a legitimate use case?
continue
}
atomic.AddUint32(&e.numFoundResults, 1)

Expand All @@ -847,7 +865,7 @@ func (e *Engine) notifyResults(ctx context.Context) {
atomic.AddUint64(&e.metrics.UnverifiedSecretsFound, 1)
}

if err := e.printer.Print(ctx, &r, &e.logVerificationErrors); err != nil {
if err := e.printer.Print(ctx, &r); err != nil {
ctx.Logger().Error(err, "error printing result")
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/output/github_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var dedupeCache = make(map[string]struct{})
// GitHubActionsPrinter is a printer that prints results in GitHub Actions format.
type GitHubActionsPrinter struct{ mu sync.Mutex }

func (p *GitHubActionsPrinter) Print(_ context.Context, r *detectors.ResultWithMetadata, _ *bool) error {
func (p *GitHubActionsPrinter) Print(_ context.Context, r *detectors.ResultWithMetadata) error {
out := gitHubActionsOutputFormat{
DetectorType: r.Result.DetectorType.String(),
DecoderType: r.Result.DecoderType.String(),
Expand Down
4 changes: 2 additions & 2 deletions pkg/output/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import (
// JSONPrinter is a printer that prints results in JSON format.
type JSONPrinter struct{ mu sync.Mutex }

func (p *JSONPrinter) Print(_ context.Context, r *detectors.ResultWithMetadata, logErrors *bool) error {
func (p *JSONPrinter) Print(_ context.Context, r *detectors.ResultWithMetadata) error {
verificationErr := func(err error) string {
if err != nil && (logErrors != nil && *logErrors) {
if err != nil {
return err.Error()
}
return ""
Expand Down
2 changes: 1 addition & 1 deletion pkg/output/legacy_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
// LegacyJSONPrinter is a printer that prints results in legacy JSON format for backwards compatibility.
type LegacyJSONPrinter struct{ mu sync.Mutex }

func (p *LegacyJSONPrinter) Print(ctx context.Context, r *detectors.ResultWithMetadata, _ *bool) error {
func (p *LegacyJSONPrinter) Print(ctx context.Context, r *detectors.ResultWithMetadata) error {
var repo string
switch r.SourceType {
case sourcespb.SourceType_SOURCE_TYPE_GIT:
Expand Down
4 changes: 2 additions & 2 deletions pkg/output/plain.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var (
// PlainPrinter is a printer that prints results in plain text format.
type PlainPrinter struct{ mu sync.Mutex }

func (p *PlainPrinter) Print(_ context.Context, r *detectors.ResultWithMetadata, logErrors *bool) error {
func (p *PlainPrinter) Print(_ context.Context, r *detectors.ResultWithMetadata) error {
out := outputFormat{
DetectorType: r.Result.DetectorType.String(),
DecoderType: r.Result.DecoderType.String(),
Expand All @@ -49,7 +49,7 @@ func (p *PlainPrinter) Print(_ context.Context, r *detectors.ResultWithMetadata,

if out.Verified {
boldGreenPrinter.Print("✅ Found verified result 🐷🔑\n")
} else if out.VerificationError != nil && (logErrors != nil && *logErrors) {
} else if out.VerificationError != nil {
printer = yellowPrinter
boldYellowPrinter.Print("⚠️ Found result - unable to verify due to error 🐷🔑❗️\n")
printer.Printf("Verification Error: %s\n", out.VerificationError)
Expand Down
2 changes: 1 addition & 1 deletion pkg/tui/pages/source_configure/trufflehog_configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (m truffleCmdModel) Cmd() string {
}

if isTrue(inputs["only-verified"].Value) {
command = append(command, "--only-verified")
command = append(command, "--results=verified")
}

if inputs["exclude_detectors"].Value != "" {
Expand Down

0 comments on commit 9b21612

Please sign in to comment.