mirror of
https://github.com/trufflesecurity/trufflehog.git
synced 2024-11-14 00:47:21 +00:00
187 lines
7.6 KiB
Go
187 lines
7.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
_ "net/http/pprof"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/felixge/fgprof"
|
|
|
|
"github.com/go-git/go-git/v5/plumbing"
|
|
"github.com/gorilla/mux"
|
|
"github.com/sirupsen/logrus"
|
|
"gopkg.in/alecthomas/kingpin.v2"
|
|
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/decoders"
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/engine"
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/output"
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/sources/git"
|
|
)
|
|
|
|
func main() {
|
|
cli := kingpin.New("TruffleHog", "TruffleHog is a tool for finding credentials.")
|
|
debug := cli.Flag("debug", "Run in debug 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()
|
|
// 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()
|
|
|
|
gitScan := cli.Command("git", "Find credentials in git repositories.")
|
|
gitScanURI := gitScan.Arg("uri", "Git repository URL. https:// or file:// schema expected.").Required().String()
|
|
gitScanIncludePaths := gitScan.Flag("include_paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String()
|
|
gitScanExcludePaths := gitScan.Flag("exclude_paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String()
|
|
gitScanSinceCommit := gitScan.Flag("since_commit", "Commit to start scan from.").String()
|
|
gitScanBranch := gitScan.Flag("branch", "Branch to scan.").String()
|
|
gitScanMaxDepth := gitScan.Flag("max_depth", "Maximum depth of commits to scan.").Int()
|
|
gitScan.Flag("allow", "No-op flag for backwards compat.").Bool()
|
|
gitScan.Flag("entropy", "No-op flag for backwards compat.").Bool()
|
|
gitScan.Flag("regex", "No-op flag for backwards compat.").Bool()
|
|
|
|
githubScan := cli.Command("github", "Find credentials in GitHub repositories.")
|
|
githubScanEndpoint := githubScan.Flag("endpoint", "GitHub endpoint.").Default("https://api.github.com").String()
|
|
githubScanRepos := githubScan.Flag("repo", `GitHub repository to scan. You can repeat this flag. Example: "https://github.com/dustin-decker/secretsandstuff"`).Strings()
|
|
githubScanOrgs := githubScan.Flag("org", `GitHub organization to scan. You can repeat this flag. Example: "trufflesecurity"`).Strings()
|
|
githubScanToken := githubScan.Flag("token", "GitHub token.").String()
|
|
githubIncludeForks := githubScan.Flag("include_forks", "Include forks in scan.").Bool()
|
|
|
|
gitlabScan := cli.Command("gitlab", "Coming soon. Find credentials in GitLab repositories.")
|
|
// gitlabScanTarget := gitlabScan.Arg("target", "GitLab target. Can be a repository, user or organization.").Required().String()
|
|
// gitlabScanToken := gitlabScan.Flag("token", "GitLab token.").String()
|
|
|
|
bitbucketScan := cli.Command("bitbucket", "Coming soon. Find credentials in Bitbucket repositories.")
|
|
// bitbucketScanTarget := bitbucketScan.Arg("target", "Bitbucket target. Can be a repository, user or organization.").Required().String()
|
|
// bitbucketScanToken := bitbucketScan.Flag("token", "Bitbucket token.").String()
|
|
|
|
filesystemScan := cli.Command("filesystem", "Coming soon. Find credentials in a filesystem.")
|
|
// filesystemScanPath := filesystemScan.Arg("path", "Path to scan.").Required().String()
|
|
// filesystemScanRecursive := filesystemScan.Flag("recursive", "Scan recursively.").Short('r').Bool()
|
|
// filesystemScanIncludePaths := filesystemScan.Flag("include_paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String()
|
|
// filesystemScanExcludePaths := filesystemScan.Flag("exclude_paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String()
|
|
|
|
s3Scan := cli.Command("s3", "Coming soon. Find credentials in an S3 bucket.")
|
|
|
|
cmd := kingpin.MustParse(cli.Parse(os.Args[1:]))
|
|
|
|
if *jsonOut {
|
|
logrus.SetFormatter(&logrus.JSONFormatter{})
|
|
}
|
|
if *debug {
|
|
logrus.SetLevel(logrus.DebugLevel)
|
|
} else {
|
|
logrus.SetLevel(logrus.InfoLevel)
|
|
}
|
|
|
|
if *debug {
|
|
go func() {
|
|
router := mux.NewRouter()
|
|
router.PathPrefix("/debug/pprof").Handler(http.DefaultServeMux)
|
|
router.PathPrefix("/debug/fgprof").Handler(fgprof.Handler())
|
|
logrus.Info("starting pprof and fgprof server on :18066 /debug/pprof and /debug/fgprof")
|
|
if err := http.ListenAndServe(":18066", router); err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
ctx := context.TODO()
|
|
e := engine.Start(ctx,
|
|
engine.WithConcurrency(*concurrency),
|
|
engine.WithDecoders(decoders.DefaultDecoders()...),
|
|
engine.WithDetectors(!*noVerification, engine.DefaultDetectors()...),
|
|
)
|
|
|
|
filter, err := common.FilterFromFiles(*gitScanIncludePaths, *gitScanExcludePaths)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("could not create filter")
|
|
}
|
|
|
|
var repoPath string
|
|
switch cmd {
|
|
case gitScan.FullCommand():
|
|
var remote bool
|
|
repoPath, remote, err = git.PrepareRepo(*gitScanURI)
|
|
if err != nil || repoPath == "" {
|
|
logrus.WithError(err).Fatal("error preparing git repo for scanning")
|
|
}
|
|
if remote {
|
|
defer os.RemoveAll(repoPath)
|
|
}
|
|
sinceHash := plumbing.NewHash(*gitScanSinceCommit)
|
|
err = e.ScanGit(ctx, repoPath, *gitScanBranch, "HEAD", &sinceHash, *gitScanMaxDepth, filter)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("Failed to scan git.")
|
|
}
|
|
case githubScan.FullCommand():
|
|
if len(*githubScanOrgs) == 0 && len(*githubScanRepos) == 0 {
|
|
log.Fatal("You must specify at least one organization or repository.")
|
|
}
|
|
err = e.ScanGitHub(ctx, *githubScanEndpoint, *githubScanRepos, *githubScanOrgs, *githubScanToken, *githubIncludeForks, filter, *concurrency)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("Failed to scan git.")
|
|
}
|
|
case gitlabScan.FullCommand():
|
|
log.Fatal("GitLab not implemented. Coming soon.")
|
|
case bitbucketScan.FullCommand():
|
|
log.Fatal("Bitbucket not implemented. Coming soon.")
|
|
case filesystemScan.FullCommand():
|
|
log.Fatal("Filesystem not implemented. Coming soon.")
|
|
case s3Scan.FullCommand():
|
|
log.Fatal("S3 not implemented. Coming soon.")
|
|
}
|
|
|
|
if !*jsonLegacy && !*jsonOut {
|
|
fmt.Fprintf(os.Stderr, "🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷\n\n")
|
|
}
|
|
|
|
for r := range e.ResultsChan() {
|
|
if *onlyVerified && !r.Verified {
|
|
continue
|
|
}
|
|
|
|
switch {
|
|
case *jsonLegacy:
|
|
legacy := output.ConvertToLegacyJSON(&r, repoPath)
|
|
out, err := json.Marshal(legacy)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("could not marshal result")
|
|
}
|
|
fmt.Println(string(out))
|
|
case *jsonOut:
|
|
out, err := json.Marshal(r)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("could not marshal result")
|
|
}
|
|
fmt.Println(string(out))
|
|
default:
|
|
output.PrintPlainOutput(&r)
|
|
}
|
|
}
|
|
logrus.Debugf("scanned %d chunks", e.ChunksScanned())
|
|
|
|
if *printAvgDetectorTime {
|
|
printAverageDetectorTime(e)
|
|
}
|
|
}
|
|
|
|
func printAverageDetectorTime(e *engine.Engine) {
|
|
fmt.Fprintln(os.Stderr, "Average detector time is the measurement of average time spent on each detector when results are returned.")
|
|
for detectorName, durations := range e.DetectorAvgTime() {
|
|
var total time.Duration
|
|
for _, d := range durations {
|
|
total += d
|
|
}
|
|
avgDuration := total / time.Duration(len(durations))
|
|
fmt.Fprintf(os.Stderr, "%s: %s\n", detectorName, avgDuration)
|
|
}
|
|
}
|