Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --results flag #2372

Merged
merged 4 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ jobs:
uses: ./
id: dogfood
with:
extra_args: --only-verified
extra_args: --results=verified,unknown
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,11 @@ Flags:
-j, --json Output in JSON format.
--json-legacy Use the pre-v3.0 JSON format. Only works with git, gitlab, and github sources.
--github-actions Output in GitHub Actions format.
--concurrency=8 Number of concurrent workers.
--concurrency=20 Number of concurrent workers.
--no-verification Don't verify the results.
--only-verified Only output verified results.
--allow-verification-overlap
Allow verification of similar credentials across detectors
--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 All @@ -279,6 +281,7 @@ Flags:
--no-update Don't check for updates.
--fail Exit with code 183 if results are found.
--verifier=VERIFIER ... Set custom verification endpoints.
--custom-verifiers-only Only use custom verification endpoints.
--archive-max-size=ARCHIVE-MAX-SIZE
Maximum size of archive to scan. (Byte units eg. 512B, 2KB, 4MB)
--archive-max-depth=ARCHIVE-MAX-DEPTH
Expand Down
66 changes: 53 additions & 13 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,20 @@ 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()
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()
results = cli.Flag("results", "Specifies which type(s) of results to output: verified, unknown, unverified. Defaults to all types.").Hidden().String()

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()
filterEntropy = cli.Flag("filter-entropy", "Filter unverified results with Shannon entropy. Start with 3.0.").Float64()
Expand Down Expand Up @@ -405,6 +407,17 @@ func run(state overseer.State) {
if *jobReportFile != nil {
jobReportWriter = *jobReportFile
}

// Parse --results flag.
if *onlyVerified {
r := "verified"
results = &r
}
parsedResults, err := parseResults(results)
rgmz marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
logFatal(err, "failed to configure results flag")
}

e, err := engine.Start(ctx,
engine.WithConcurrency(*concurrency),
engine.WithDecoders(decoders.DefaultDecoders()...),
Expand All @@ -415,7 +428,7 @@ func run(state overseer.State) {
engine.WithFilterDetectors(excludeFilter),
engine.WithFilterDetectors(endpointCustomizer),
engine.WithFilterUnverified(*filterUnverified),
engine.WithOnlyVerified(*onlyVerified),
engine.WithResults(parsedResults),
engine.WithPrintAvgDetectorTime(*printAvgDetectorTime),
engine.WithPrinter(printer),
engine.WithFilterEntropy(*filterEntropy),
Expand Down Expand Up @@ -594,6 +607,33 @@ func run(state overseer.State) {
}
}

// parseResults ensures that users provide valid CSV input to `--results`.
//
// This is a work-around to kingpin not supporting CSVs.
// See: https://github.com/trufflesecurity/trufflehog/pull/2372#issuecomment-1983868917
func parseResults(input *string) (map[string]struct{}, error) {
if *input == "" {
return nil, nil
}


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like an extra newline

var (
values = strings.Split(strings.ToLower(*input), ",")
results = make(map[string]struct{}, 3)
)
for _, value := range values {
switch value {
case "verified":
case "unknown":
case "unverified":
results[value] = struct{}{}
default:
return nil, fmt.Errorf("invalid value '%s', valid values are 'verified,unknown,unverified'", value)
}
}
return results, nil
}

// logFatalFunc returns a log.Fatal style function. Calling the returned
// function will terminate the program without cleanup.
func logFatalFunc(logger logr.Logger) func(error, string, ...any) {
Expand Down
46 changes: 37 additions & 9 deletions pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,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
verificationOverlap bool
printAvgDetectorTime bool
filterEntropy *float64
notifyVerifiedResults bool
notifyUnverifiedResults bool
notifyUnknownResults bool
verificationOverlap bool
printAvgDetectorTime bool

// ahoCorasickHandler manages the Aho-Corasick trie and related keyword lookups.
ahoCorasickCore *ahocorasick.AhoCorasickCore
Expand Down Expand Up @@ -164,11 +166,24 @@ 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 {
// WithResults defines which results will be printed by the engine.
func WithResults(results map[string]struct{}) Option {
return func(e *Engine) {
e.onlyVerified = onlyVerified
if len(results) > 0 {
if _, ok := results["verified"]; ok {
e.notifyVerifiedResults = true
}
if _, ok := results["unknown"]; ok {
e.notifyUnknownResults = true
}
if _, ok := results["unverified"]; ok {
e.notifyUnverifiedResults = true
}
} else {
e.notifyVerifiedResults = true
e.notifyUnknownResults = true
e.notifyUnverifiedResults = true
}
rgmz marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -849,7 +864,20 @@ func (e *Engine) processResult(ctx context.Context, data detectableChunk, res de

func (e *Engine) notifyResults(ctx context.Context) {
for r := range e.ResultsChan() {
if e.onlyVerified && !r.Verified {
// Filter unwanted results, based on `--results`.
if !r.Verified {
if r.VerificationError() != nil {
if !e.notifyUnknownResults {
// Skip results with verification errors.
continue
}
} else if !e.notifyUnverifiedResults {
// Skip unverified results.
continue
}
} else if !e.notifyVerifiedResults {
// Skip verified results.
// TODO: Is this a legitimate use case?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can imagine someone running with --results=unknown to check Trufflehog's network access.

continue
}
atomic.AddUint32(&e.numFoundResults, 1)
Expand Down
2 changes: 1 addition & 1 deletion pkg/output/plain.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (p *PlainPrinter) Print(_ context.Context, r *detectors.ResultWithMetadata)
boldGreenPrinter.Print("✅ Found verified result 🐷🔑\n")
} else if out.VerificationError != nil {
printer = yellowPrinter
boldYellowPrinter.Print("⚠️ Found result - unable to verify due to error 🐷🔑❗️\n")
boldYellowPrinter.Print("⚠️ Found result - unable to verify due to error 🐷🔑❗️\n")
printer.Printf("Verification Error: %s\n", out.VerificationError)
} else {
printer = whitePrinter
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
Loading