Skip to content
Open
Show file tree
Hide file tree
Changes from all 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: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ 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.
--github-actions-step-summary
Output a summary to the GitHub Actions step summary.
--concurrency=20 Number of concurrent workers.
--no-verification Don't verify the results.
--results=RESULTS Specifies which type(s) of results to output: verified (confirmed valid by API), unknown (verification failed due to error), unverified (detected but not verified), filtered_unverified (unverified but would have been filtered out). Defaults to all types.
Expand Down
156 changes: 86 additions & 70 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,85 +22,101 @@ inputs:
default: "latest"
description: Scan with this trufflehog cli version.
required: false
generate_step_summary:
default: "true"
description: Output results to the GitHub Actions step summary.
required: false
branding:
icon: "shield"
color: "green"

runs:
using: "composite"
steps:
- shell: bash
working-directory: ${{ inputs.path }}
env:
BASE: ${{ inputs.base }}
HEAD: ${{ inputs.head }}
ARGS: ${{ inputs.extra_args }}
COMMIT_IDS: ${{ toJson(github.event.commits.*.id) }}
VERSION: ${{ inputs.version }}
run: |
##########################################
## ADVANCED USAGE ##
## Scan by BASE & HEAD user inputs ##
## If BASE == HEAD, exit with error ##
##########################################
# Check if jq is installed, if not, install it
if ! command -v jq &> /dev/null
then
echo "jq could not be found, installing..."
apt-get -y update && apt-get install -y jq
fi
- shell: bash
working-directory: ${{ inputs.path }}
env:
BASE: ${{ inputs.base }}
HEAD: ${{ inputs.head }}
ARGS: ${{ inputs.extra_args }}
COMMIT_IDS: ${{ toJson(github.event.commits.*.id) }}
VERSION: ${{ inputs.version }}
GITHUB_STEP_SUMMARY_ENABLED: ${{ inputs.generate_step_summary }}
run: |
##########################################
## ADVANCED USAGE ##
## Scan by BASE & HEAD user inputs ##
## If BASE == HEAD, exit with error ##
##########################################
# Check if jq is installed, if not, install it
if ! command -v jq &> /dev/null
then
echo "jq could not be found, installing..."
apt-get -y update && apt-get install -y jq
fi

git status >/dev/null # make sure we are in a git repository
if [ -n "$BASE" ] || [ -n "$HEAD" ]; then
if [ -n "$BASE" ]; then
base_commit=$(git rev-parse "$BASE" 2>/dev/null) || true
else
base_commit=""
fi
if [ -n "$HEAD" ]; then
head_commit=$(git rev-parse "$HEAD" 2>/dev/null) || true
else
head_commit=""
fi
if [ "$base_commit" == "$head_commit" ] ; then
echo "::error::BASE and HEAD commits are the same. TruffleHog won't scan anything. Please see documentation (https://github.com/trufflesecurity/trufflehog#octocat-trufflehog-github-action)."
exit 1
fi
##########################################
## Scan commits based on event type ##
##########################################
git status >/dev/null # make sure we are in a git repository
if [ -n "$BASE" ] || [ -n "$HEAD" ]; then
if [ -n "$BASE" ]; then
base_commit=$(git rev-parse "$BASE" 2>/dev/null) || true
else
if [ "${{ github.event_name }}" == "push" ]; then
COMMIT_LENGTH=$(printenv COMMIT_IDS | jq length)
if [ $COMMIT_LENGTH == "0" ]; then
echo "No commits to scan"
exit 0
fi
HEAD=${{ github.event.after }}
if [ ${{ github.event.before }} == "0000000000000000000000000000000000000000" ]; then
BASE=""
else
BASE=${{ github.event.before }}
fi
elif [ "${{ github.event_name }}" == "workflow_dispatch" ] || [ "${{ github.event_name }}" == "schedule" ]; then
base_commit=""
fi
if [ -n "$HEAD" ]; then
head_commit=$(git rev-parse "$HEAD" 2>/dev/null) || true
else
head_commit=""
fi
if [ "$base_commit" == "$head_commit" ] ; then
echo "::error::BASE and HEAD commits are the same. TruffleHog won't scan anything. Please see documentation (https://github.com/trufflesecurity/trufflehog#octocat-trufflehog-github-action)."
exit 1
fi
##########################################
## Scan commits based on event type ##
##########################################
else
if [ "${{ github.event_name }}" == "push" ]; then
COMMIT_LENGTH=$(printenv COMMIT_IDS | jq length)
if [ $COMMIT_LENGTH == "0" ]; then
echo "No commits to scan"
exit 0
fi
HEAD=${{ github.event.after }}
if [ ${{ github.event.before }} == "0000000000000000000000000000000000000000" ]; then
BASE=""
HEAD=""
elif [ "${{ github.event_name }}" == "pull_request" ]; then
BASE=${{github.event.pull_request.base.sha}}
HEAD=${{github.event.pull_request.head.sha}}
else
BASE=${{ github.event.before }}
fi
elif [ "${{ github.event_name }}" == "workflow_dispatch" ] || [ "${{ github.event_name }}" == "schedule" ]; then
BASE=""
HEAD=""
elif [ "${{ github.event_name }}" == "pull_request" ]; then
BASE=${{github.event.pull_request.base.sha}}
HEAD=${{github.event.pull_request.head.sha}}
fi
##########################################
## Run TruffleHog ##
##########################################
docker run --rm -v .:/tmp -w /tmp \
ghcr.io/trufflesecurity/trufflehog:${VERSION} \
git file:///tmp/ \
--since-commit \
${BASE:-''} \
--branch \
${HEAD:-''} \
--fail \
--no-update \
--github-actions \
${ARGS:-''}
fi
##########################################
## Run TruffleHog ##
##########################################
STEP_SUMMARY_FLAG=""
if [ "$GITHUB_STEP_SUMMARY_ENABLED" == "true" ]; then
STEP_SUMMARY_FLAG="--github-actions-step-summary"
fi
docker run --rm -v .:/tmp -w /tmp \
-e GITHUB_STEP_SUMMARY=/tmp/.step_summary \
ghcr.io/trufflesecurity/trufflehog:${VERSION} \
git file:///tmp/ \
--since-commit \
${BASE:-''} \
--branch \
${HEAD:-''} \
--fail \
--no-update \
--github-actions \
${STEP_SUMMARY_FLAG} \
${ARGS:-''}
# Append the step summary file if it exists
if [ -f ".step_summary" ]; then
cat .step_summary >> $GITHUB_STEP_SUMMARY
rm .step_summary
fi
38 changes: 24 additions & 14 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,21 @@ var (
cli = kingpin.New("TruffleHog", "TruffleHog is a tool for finding credentials.")
cmd string
// https://github.com/trufflesecurity/trufflehog/blob/main/CONTRIBUTING.md#logging-in-trufflehog
logLevel = cli.Flag("log-level", `Logging verbosity on a scale of 0 (info) to 5 (trace). Can be disabled with "-1".`).Default("0").Int()
debug = cli.Flag("debug", "Run in debug mode.").Hidden().Bool()
trace = cli.Flag("trace", "Run in trace mode.").Hidden().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 (confirmed valid by API), unknown (verification failed due to error), unverified (detected but not verified), filtered_unverified (unverified but would have been filtered out). Defaults to verified,unverified,unknown.").String()
noColor = cli.Flag("no-color", "Disable colorized output").Bool()
noColour = cli.Flag("no-colour", "Alias for --no-color").Hidden().Bool()
logLevel = cli.Flag("log-level", `Logging verbosity on a scale of 0 (info) to 5 (trace). Can be disabled with "-1".`).Default("0").Int()
debug = cli.Flag("debug", "Run in debug mode.").Hidden().Bool()
trace = cli.Flag("trace", "Run in trace mode.").Hidden().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()
gitHubActionsStepSummaryFormat = cli.Flag("github-actions-step-summary", "Output a summary to the GitHub Actions step summary.").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 (confirmed valid by API), unknown (verification failed due to error), unverified (detected but not verified), filtered_unverified (unverified but would have been filtered out). Defaults to verified,unverified,unknown.").String()
noColor = cli.Flag("no-color", "Disable colorized output").Bool()
noColour = cli.Flag("no-colour", "Alias for --no-color").Hidden().Bool()

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 @@ -510,6 +511,8 @@ func run(state overseer.State) {
printer = new(output.JSONPrinter)
case *gitHubActionsFormat:
printer = new(output.GitHubActionsPrinter)
case *gitHubActionsStepSummaryFormat:
printer = new(output.GitHubActionsStepSummaryPrinter)
default:
printer = new(output.PlainPrinter)
}
Expand Down Expand Up @@ -579,6 +582,13 @@ func run(state overseer.State) {
logFatal(err, "error running scan")
}

// Finish the printer if it implements Finisher (e.g., for step summary output).
if dispatcher, ok := engConf.Dispatcher.(*engine.PrinterDispatcher); ok {
if err := dispatcher.Finish(); err != nil {
logger.Error(err, "error finishing printer")
}
}

verificationCacheMetricsSnapshot := struct {
Hits int32
Misses int32
Expand Down
14 changes: 14 additions & 0 deletions pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ type Printer interface {
Print(ctx context.Context, r *detectors.ResultWithMetadata) error
}

// Finisher is an optional interface that Printers can implement to perform cleanup
// or write final output after all results have been printed.
type Finisher interface {
Finish() error
}

// PrinterDispatcher wraps an existing Printer implementation and adapts it to the ResultsDispatcher interface.
type PrinterDispatcher struct{ printer Printer }

Expand All @@ -93,6 +99,14 @@ func (p *PrinterDispatcher) Dispatch(ctx context.Context, result detectors.Resul
return p.printer.Print(ctx, &result)
}

// Finish calls Finish on the underlying printer if it implements the Finisher interface.
func (p *PrinterDispatcher) Finish() error {
if f, ok := p.printer.(Finisher); ok {
return f.Finish()
}
return nil
}

// Config used to configure the engine.
type Config struct {
// Number of concurrent scanner workers,
Expand Down
Loading
Loading