|
#!/bin/bash |
|
|
|
# TruffleHog Security Scanner |
|
# Scans filesystem for secrets and outputs filtered JSON results |
|
|
|
set -euo pipefail # Exit on error, undefined vars, pipe failures |
|
|
|
# Global variables for cleanup |
|
TRUFFLEHOG_LOG="" |
|
CLEANUP_NEEDED=false |
|
|
|
# Signal handling for graceful cleanup |
|
# This function is called when the script receives SIGINT (Ctrl+C) or SIGTERM |
|
# It ensures temporary files are cleaned up before exit |
|
cleanup() { |
|
if [ "$CLEANUP_NEEDED" = true ]; then |
|
log_info "Cleaning up temporary files..." |
|
if [ -n "$TRUFFLEHOG_LOG" ] && [ -f "$TRUFFLEHOG_LOG" ]; then |
|
rm -f "$TRUFFLEHOG_LOG" |
|
log_verbose "Removed temporary log file: $TRUFFLEHOG_LOG" |
|
fi |
|
CLEANUP_NEEDED=false |
|
fi |
|
log_info "Script terminated" |
|
exit 130 # Standard exit code for SIGINT |
|
} |
|
|
|
# Set up signal handlers |
|
trap cleanup SIGINT SIGTERM |
|
|
|
# Default values |
|
SCAN_DIR="" |
|
OUTPUT_FILE="" |
|
EXCLUDE_FILE="exclude-paths.txt" |
|
DEBUG=false |
|
VERBOSE=false |
|
QUIET=false |
|
NO_VERIFICATION=true |
|
LOG_LEVEL="-1" |
|
GENERATE_SUMMARY=true |
|
|
|
# Color codes |
|
RED='\033[0;31m' |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
BLUE='\033[0;34m' |
|
MAGENTA='\033[0;35m' |
|
CYAN='\033[0;36m' |
|
WHITE='\033[1;37m' |
|
BOLD='\033[1m' |
|
RESET='\033[0m' |
|
|
|
# Check if colors should be used |
|
USE_COLORS=true |
|
if [ -n "${NO_COLOR:-}" ] || [ "${TERM:-}" = "dumb" ]; then |
|
USE_COLORS=false |
|
elif [ -t 2 ]; then |
|
# Check if stderr is a terminal (where we output logs) |
|
USE_COLORS=true |
|
else |
|
USE_COLORS=false |
|
fi |
|
|
|
# Logging functions |
|
# Color-coded logging system with timestamp and severity levels |
|
# Supports both colored and plain text output based on terminal capabilities |
|
|
|
# Log informational messages (blue) |
|
log_info() { |
|
if [ "$QUIET" = false ]; then |
|
if [ "$USE_COLORS" = true ]; then |
|
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${RESET} ${BOLD}INFO:${RESET} $1" >&2 |
|
else |
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1" >&2 |
|
fi |
|
fi |
|
} |
|
|
|
# Log warning messages (yellow) |
|
log_warn() { |
|
if [ "$QUIET" = false ]; then |
|
if [ "$USE_COLORS" = true ]; then |
|
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')]${RESET} ${BOLD}WARN:${RESET} $1" >&2 |
|
else |
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARN: $1" >&2 |
|
fi |
|
fi |
|
} |
|
|
|
# Log error messages (red) - always shown even in quiet mode |
|
log_error() { |
|
if [ "$USE_COLORS" = true ]; then |
|
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')]${RESET} ${BOLD}ERROR:${RESET} $1" >&2 |
|
else |
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >&2 |
|
fi |
|
} |
|
|
|
# Log success messages (green) |
|
log_success() { |
|
if [ "$QUIET" = false ]; then |
|
if [ "$USE_COLORS" = true ]; then |
|
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${RESET} ${BOLD}SUCCESS:${RESET} $1" >&2 |
|
else |
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] SUCCESS: $1" >&2 |
|
fi |
|
fi |
|
} |
|
|
|
# Log debug messages (magenta) - only shown in debug mode |
|
log_debug() { |
|
if [ "$DEBUG" = true ]; then |
|
if [ "$USE_COLORS" = true ]; then |
|
echo -e "${MAGENTA}[$(date '+%Y-%m-%d %H:%M:%S')]${RESET} ${BOLD}DEBUG:${RESET} $1" >&2 |
|
else |
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] DEBUG: $1" >&2 |
|
fi |
|
fi |
|
} |
|
|
|
# Log verbose messages (cyan) - only shown in verbose mode |
|
log_verbose() { |
|
if [ "$VERBOSE" = true ] && [ "$QUIET" = false ]; then |
|
if [ "$USE_COLORS" = true ]; then |
|
echo -e "${CYAN}[$(date '+%Y-%m-%d %H:%M:%S')]${RESET} ${BOLD}VERBOSE:${RESET} $1" >&2 |
|
else |
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] VERBOSE: $1" >&2 |
|
fi |
|
fi |
|
} |
|
|
|
# Help function |
|
show_help() { |
|
cat << EOF |
|
TruffleHog Security Scanner |
|
|
|
USAGE: |
|
$0 [OPTIONS] <scan_directory> <output_file> |
|
|
|
ARGUMENTS: |
|
scan_directory Directory to scan for secrets |
|
output_file Output JSON file for results |
|
|
|
OPTIONS: |
|
-e, --exclude FILE Exclude patterns file (default: exclude-paths.txt) |
|
-d, --debug Enable debug logging |
|
-v, --verbose Enable verbose logging |
|
-q, --quiet Suppress all output except errors |
|
-V, --verify Enable verification of found secrets |
|
-l, --log-level LEVEL Set TruffleHog log level (default: -1) |
|
--no-color Disable colored output |
|
--no-summary Skip generation of summary report |
|
-h, --help Show this help message |
|
|
|
EXAMPLES: |
|
$0 /path/to/project results.json |
|
$0 -v -e custom-excludes.txt /path/to/project results.json |
|
$0 --debug --verify /path/to/project results.json |
|
$0 -q /path/to/project results.json > /dev/null |
|
|
|
ENVIRONMENT VARIABLES: |
|
DEBUG=1 Enable debug logging (same as -d) |
|
VERBOSE=1 Enable verbose logging (same as -v) |
|
QUIET=1 Suppress output (same as -q) |
|
NO_COLOR=1 Disable colored output |
|
|
|
EOF |
|
} |
|
|
|
# Parse command line arguments |
|
# Processes all command line options and positional arguments |
|
# Sets global variables for script configuration |
|
parse_arguments() { |
|
while [[ $# -gt 0 ]]; do |
|
case $1 in |
|
-e|--exclude) |
|
if [ $# -lt 2 ]; then |
|
log_error "Option $1 requires a value" |
|
echo "Use -h or --help for usage information" |
|
exit 1 |
|
fi |
|
EXCLUDE_FILE="$2" |
|
shift 2 |
|
;; |
|
-d|--debug) |
|
DEBUG=true |
|
shift |
|
;; |
|
-v|--verbose) |
|
VERBOSE=true |
|
shift |
|
;; |
|
-q|--quiet) |
|
QUIET=true |
|
shift |
|
;; |
|
-V|--verify) |
|
NO_VERIFICATION=false |
|
shift |
|
;; |
|
-l|--log-level) |
|
if [ $# -lt 2 ]; then |
|
log_error "Option $1 requires a value" |
|
echo "Use -h or --help for usage information" |
|
exit 1 |
|
fi |
|
LOG_LEVEL="$2" |
|
shift 2 |
|
;; |
|
--no-color) |
|
USE_COLORS=false |
|
shift |
|
;; |
|
--no-summary) |
|
GENERATE_SUMMARY=false |
|
shift |
|
;; |
|
-h|--help) |
|
show_help |
|
exit 0 |
|
;; |
|
-*) |
|
log_error "Unknown option: $1" |
|
echo "Use -h or --help for usage information" |
|
exit 1 |
|
;; |
|
*) |
|
if [ -z "$SCAN_DIR" ]; then |
|
SCAN_DIR="$1" |
|
elif [ -z "$OUTPUT_FILE" ]; then |
|
OUTPUT_FILE="$1" |
|
else |
|
log_error "Too many arguments" |
|
echo "Use -h or --help for usage information" |
|
exit 1 |
|
fi |
|
shift |
|
;; |
|
esac |
|
done |
|
} |
|
|
|
# Check environment variables |
|
# Processes environment variables that can override command line options |
|
# Handles conflicts between different logging modes |
|
check_environment() { |
|
if [ "${DEBUG:-0}" = "1" ]; then |
|
DEBUG=true |
|
fi |
|
if [ "${VERBOSE:-0}" = "1" ]; then |
|
VERBOSE=true |
|
fi |
|
if [ "${QUIET:-0}" = "1" ]; then |
|
QUIET=true |
|
fi |
|
if [ -n "${NO_COLOR:-}" ]; then |
|
USE_COLORS=false |
|
fi |
|
|
|
# Check for conflicting flags |
|
if [ "$QUIET" = true ] && [ "$VERBOSE" = true ]; then |
|
log_warn "Both --quiet and --verbose specified, --quiet takes precedence" |
|
VERBOSE=false |
|
fi |
|
|
|
if [ "$QUIET" = true ] && [ "$DEBUG" = true ]; then |
|
log_warn "Both --quiet and --debug specified, --quiet takes precedence" |
|
DEBUG=false |
|
fi |
|
} |
|
|
|
# Validate arguments |
|
validate_arguments() { |
|
if [ -z "$SCAN_DIR" ] || [ -z "$OUTPUT_FILE" ]; then |
|
log_error "Missing required arguments" |
|
log_error "Required: scan_directory and output_file" |
|
echo "Use -h or --help for usage information" |
|
exit 1 |
|
fi |
|
|
|
# Validate scan directory path format |
|
if [[ "$SCAN_DIR" =~ ^- ]]; then |
|
log_error "Scan directory cannot start with '-' (looks like an option)" |
|
log_error "Use './$SCAN_DIR' or absolute path if directory starts with '-'" |
|
exit 1 |
|
fi |
|
|
|
# Validate output file path format |
|
if [[ "$OUTPUT_FILE" =~ ^- ]]; then |
|
log_error "Output file cannot start with '-' (looks like an option)" |
|
log_error "Use './$OUTPUT_FILE' or absolute path if filename starts with '-'" |
|
exit 1 |
|
fi |
|
|
|
# Check for relative paths and warn |
|
if [[ "$SCAN_DIR" != /* ]]; then |
|
log_verbose "Using relative path for scan directory: $SCAN_DIR" |
|
fi |
|
|
|
if [[ "$OUTPUT_FILE" != /* ]]; then |
|
log_verbose "Using relative path for output file: $OUTPUT_FILE" |
|
fi |
|
} |
|
|
|
# Validation functions |
|
# Comprehensive input validation including file/directory existence and permissions |
|
# Validates scan directory, exclude file, and output file paths |
|
validate_inputs() { |
|
log_verbose "Validating inputs..." |
|
|
|
if [ ! -d "$SCAN_DIR" ]; then |
|
log_error "Scan directory '$SCAN_DIR' does not exist or is not accessible" |
|
log_error "Please verify the path exists and you have read permissions" |
|
log_error "Use 'ls -la \"$SCAN_DIR\"' to check directory status" |
|
exit 1 |
|
fi |
|
|
|
# Check read permissions on scan directory |
|
if [ ! -r "$SCAN_DIR" ]; then |
|
log_error "No read permission for scan directory: $SCAN_DIR" |
|
log_error "Use 'ls -la \"$SCAN_DIR\"' to check permissions" |
|
exit 1 |
|
fi |
|
|
|
# Check if directory is empty (warn but don't fail) |
|
if [ -z "$(ls -A "$SCAN_DIR" 2>/dev/null)" ]; then |
|
log_warn "Scan directory '$SCAN_DIR' is empty" |
|
log_warn "No files will be scanned" |
|
fi |
|
|
|
# Handle relative paths for exclude file |
|
if [[ "$EXCLUDE_FILE" != /* ]]; then |
|
# Relative path - make it relative to script directory |
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|
EXCLUDE_FILE="$SCRIPT_DIR/$EXCLUDE_FILE" |
|
fi |
|
|
|
if [ ! -f "$EXCLUDE_FILE" ]; then |
|
log_error "Exclude patterns file '$EXCLUDE_FILE' not found" |
|
log_error "This file should contain patterns to exclude from scanning (one per line)" |
|
log_error "Create the file or specify a different path with -e/--exclude option" |
|
exit 1 |
|
fi |
|
|
|
# Validate exclude file is readable |
|
if [ ! -r "$EXCLUDE_FILE" ]; then |
|
log_error "No read permission for exclude file: $EXCLUDE_FILE" |
|
exit 1 |
|
fi |
|
|
|
# Check if exclude file is empty (warn but don't fail) |
|
if [ ! -s "$EXCLUDE_FILE" ]; then |
|
log_warn "Exclude patterns file '$EXCLUDE_FILE' is empty" |
|
log_warn "No files will be excluded from scanning" |
|
fi |
|
|
|
log_verbose "Input validation passed" |
|
} |
|
|
|
# Check system dependencies |
|
# Verifies that required tools (trufflehog, jq) are installed and accessible |
|
# Provides installation instructions if dependencies are missing |
|
check_dependencies() { |
|
log_verbose "Checking dependencies..." |
|
|
|
if ! command -v trufflehog &> /dev/null; then |
|
log_error "TruffleHog is not installed or not in PATH" |
|
log_error "Current PATH: $PATH" |
|
log_info "Installation options:" |
|
log_info " macOS: brew install trufflehog" |
|
log_info " Go: go install github.com/trufflesecurity/trufflehog/v3/cmd/trufflehog@latest" |
|
log_info " Check: https://trufflesecurity.com/trufflehog/docs/installation" |
|
exit 1 |
|
fi |
|
|
|
if ! command -v jq &> /dev/null; then |
|
log_error "jq is not installed or not in PATH" |
|
log_error "jq is required for JSON processing of TruffleHog output" |
|
log_info "Installation options:" |
|
log_info " macOS: brew install jq" |
|
log_info " Ubuntu/Debian: sudo apt-get install jq" |
|
log_info " CentOS/RHEL: sudo yum install jq" |
|
log_info " Check: https://stedolan.github.io/jq/download/" |
|
exit 1 |
|
fi |
|
|
|
log_verbose "Dependencies check passed" |
|
} |
|
|
|
# Prepare output directory and file |
|
# Creates output directory if it doesn't exist and validates write permissions |
|
# Tests write access to the output file location |
|
prepare_output() { |
|
log_verbose "Preparing output directory..." |
|
|
|
OUTPUT_DIR="$(dirname "$OUTPUT_FILE")" |
|
if [ ! -d "$OUTPUT_DIR" ]; then |
|
log_info "Creating output directory: $OUTPUT_DIR" |
|
if ! mkdir -p "$OUTPUT_DIR"; then |
|
log_error "Failed to create output directory: $OUTPUT_DIR" |
|
exit 1 |
|
fi |
|
fi |
|
|
|
# Check write permissions |
|
if [ ! -w "$OUTPUT_DIR" ]; then |
|
log_error "No write permission for output directory: $OUTPUT_DIR" |
|
exit 1 |
|
fi |
|
|
|
# Test if we can write to the output file location |
|
if ! touch "$OUTPUT_FILE" 2>/dev/null; then |
|
log_error "Cannot write to output file: $OUTPUT_FILE" |
|
log_error "Check if the directory exists and you have write permissions" |
|
log_error "Use 'ls -la \"$(dirname "$OUTPUT_FILE")\"' to check directory permissions" |
|
exit 1 |
|
fi |
|
|
|
# Remove test file |
|
rm -f "$OUTPUT_FILE" |
|
|
|
log_verbose "Output directory ready" |
|
} |
|
|
|
|
|
# Execute TruffleHog security scan |
|
# Builds command arguments, runs TruffleHog with robust JSON processing |
|
# Handles errors and cleans up temporary files |
|
run_scan() { |
|
log_info "Starting TruffleHog scan..." |
|
|
|
# Build TruffleHog command arguments |
|
TRUFFLEHOG_ARGS=( |
|
"filesystem" |
|
"--exclude-paths=$EXCLUDE_FILE" |
|
"--json" |
|
"--log-level=$LOG_LEVEL" |
|
) |
|
|
|
if [ "$NO_VERIFICATION" = true ]; then |
|
TRUFFLEHOG_ARGS+=("--no-verification") |
|
fi |
|
|
|
TRUFFLEHOG_ARGS+=("$SCAN_DIR") |
|
|
|
log_debug "Command: trufflehog ${TRUFFLEHOG_ARGS[*]}" |
|
|
|
# Create unique log file to avoid race conditions |
|
TRUFFLEHOG_LOG="/tmp/trufflehog_$(date +%Y%m%d_%H%M%S)_$$.log" |
|
CLEANUP_NEEDED=true |
|
|
|
# Run TruffleHog scan with error handling |
|
# Use robust JSON processing that handles multi-line and malformed JSON |
|
if ! trufflehog "${TRUFFLEHOG_ARGS[@]}" 2>"$TRUFFLEHOG_LOG" | \ |
|
jq -R 'select(length > 0) | try fromjson catch empty | select(.Raw != null and .Raw != "") | { |
|
DetectorName: .DetectorName, |
|
file: .SourceMetadata.Data.Filesystem.file, |
|
Raw: .Raw |
|
}' | jq -s '.' > "$OUTPUT_FILE"; then |
|
log_error "TruffleHog scan failed" |
|
log_error "This could be due to:" |
|
log_error " - Insufficient permissions to read files" |
|
log_error " - Corrupted exclude patterns file" |
|
log_error " - TruffleHog internal error" |
|
if [ -f "$TRUFFLEHOG_LOG" ]; then |
|
log_error "TruffleHog error details:" |
|
cat "$TRUFFLEHOG_LOG" >&2 |
|
rm -f "$TRUFFLEHOG_LOG" |
|
else |
|
log_error "No error log available - check TruffleHog installation" |
|
fi |
|
exit 1 |
|
fi |
|
|
|
# Clean up log file and mark cleanup as complete |
|
rm -f "$TRUFFLEHOG_LOG" |
|
CLEANUP_NEEDED=false |
|
log_verbose "TruffleHog scan completed" |
|
} |
|
|
|
# Process and report scan results |
|
# Validates JSON output, counts results, calculates execution time |
|
# Provides summary statistics and file size information |
|
process_results() { |
|
log_verbose "Processing results..." |
|
|
|
# Calculate execution time |
|
END_TIME=$(date +%s) |
|
DURATION=$((END_TIME - START_TIME)) |
|
MINUTES=$((DURATION / 60)) |
|
SECONDS=$((DURATION % 60)) |
|
|
|
# Count results with JSON validation |
|
if [ -f "$OUTPUT_FILE" ]; then |
|
# Validate JSON format first |
|
if ! jq empty "$OUTPUT_FILE" 2>/dev/null; then |
|
log_error "Output file contains invalid JSON format" |
|
log_error "This may indicate a problem with the TruffleHog scan or JSON processing" |
|
exit 1 |
|
fi |
|
|
|
# Count results safely |
|
RESULT_COUNT=$(jq 'length' "$OUTPUT_FILE" 2>/dev/null || echo "0") |
|
FILE_SIZE=$(du -h "$OUTPUT_FILE" | cut -f1) |
|
|
|
if [ "$RESULT_COUNT" -gt 0 ]; then |
|
log_warn "Found $RESULT_COUNT potential secrets in $MINUTES minutes and $SECONDS seconds" |
|
log_warn "Results saved to: $OUTPUT_FILE ($FILE_SIZE)" |
|
log_info "Review the results carefully and take appropriate action" |
|
else |
|
log_success "No secrets found in $MINUTES minutes and $SECONDS seconds" |
|
log_success "Results saved to: $OUTPUT_FILE ($FILE_SIZE)" |
|
fi |
|
else |
|
log_error "Output file was not created" |
|
exit 1 |
|
fi |
|
} |
|
|
|
# Generate summary report from JSON output |
|
# Creates a comprehensive summary with statistics, top detectors, and file analysis |
|
generate_summary() { |
|
log_verbose "Generating summary report..." |
|
|
|
if [ ! -f "$OUTPUT_FILE" ] || [ ! -s "$OUTPUT_FILE" ]; then |
|
log_warn "No output file available for summary generation" |
|
return 0 |
|
fi |
|
|
|
# Create summary file path |
|
SUMMARY_FILE="${OUTPUT_FILE%.*}_summary.txt" |
|
|
|
{ |
|
echo "==========================================" |
|
echo "TRUFFLEHOG SECURITY SCAN SUMMARY" |
|
echo "==========================================" |
|
echo "Generated: $(date '+%Y-%m-%d %H:%M:%S')" |
|
echo "Scan Directory: $SCAN_DIR" |
|
echo "Output File: $OUTPUT_FILE" |
|
echo "" |
|
|
|
# Basic statistics |
|
TOTAL_FINDINGS=$(jq 'length' "$OUTPUT_FILE" 2>/dev/null || echo "0") |
|
echo "TOTAL FINDINGS: $TOTAL_FINDINGS" |
|
echo "" |
|
|
|
if [ "$TOTAL_FINDINGS" -eq 0 ]; then |
|
echo "✅ No secrets found - scan completed successfully!" |
|
echo "" |
|
echo "This indicates that no potential secrets were detected" |
|
echo "in the scanned directory. However, please note that:" |
|
echo "- This is a static analysis and may not catch all secrets" |
|
echo "- Review the exclude patterns to ensure nothing important was skipped" |
|
echo "- Consider running additional security scans" |
|
else |
|
echo "⚠️ SECRETS DETECTED - IMMEDIATE ACTION REQUIRED!" |
|
echo "" |
|
|
|
# Top detectors |
|
echo "TOP DETECTORS:" |
|
echo "--------------" |
|
jq -r '.[].DetectorName' "$OUTPUT_FILE" 2>/dev/null | sort | uniq -c | sort -nr | head -10 | \ |
|
while read count detector; do |
|
printf "%-4s %s\n" "$count" "$detector" |
|
done |
|
echo "" |
|
|
|
# Files with most findings |
|
echo "FILES WITH MOST FINDINGS:" |
|
echo "------------------------" |
|
jq -r '.[].file' "$OUTPUT_FILE" 2>/dev/null | sort | uniq -c | sort -nr | head -10 | \ |
|
while read count file; do |
|
printf "%-4s %s\n" "$count" "$file" |
|
done |
|
echo "" |
|
|
|
# File extensions analysis |
|
echo "FILE EXTENSIONS ANALYSIS:" |
|
echo "------------------------" |
|
jq -r '.[].file' "$OUTPUT_FILE" 2>/dev/null | sed 's/.*\.//' | sort | uniq -c | sort -nr | head -10 | \ |
|
while read count ext; do |
|
if [ -n "$ext" ] && [ "$ext" != "null" ]; then |
|
printf "%-4s .%s\n" "$count" "$ext" |
|
else |
|
printf "%-4s (no extension)\n" "$count" |
|
fi |
|
done |
|
echo "" |
|
|
|
# Severity analysis (based on detector types) |
|
echo "DETECTOR CATEGORIES:" |
|
echo "-------------------" |
|
jq -r '.[].DetectorName' "$OUTPUT_FILE" 2>/dev/null | \ |
|
while read detector; do |
|
case "$detector" in |
|
*"AWS"*|*"GCP"*|*"Azure"*) echo "Cloud Credentials" ;; |
|
*"API"*|*"Token"*|*"Key"*) echo "API Keys & Tokens" ;; |
|
*"Password"*|*"Secret"*) echo "Passwords & Secrets" ;; |
|
*"Database"*|*"DB"*) echo "Database Credentials" ;; |
|
*"SSH"*|*"RSA"*|*"Private"*) echo "SSH Keys" ;; |
|
*"GitHub"*|*"GitLab"*|*"Bitbucket"*) echo "Git Credentials" ;; |
|
*) echo "Other" ;; |
|
esac |
|
done | sort | uniq -c | sort -nr | \ |
|
while read count category; do |
|
printf "%-4s %s\n" "$count" "$category" |
|
done |
|
echo "" |
|
|
|
# Recommendations |
|
echo "RECOMMENDATIONS:" |
|
echo "----------------" |
|
echo "1. IMMEDIATELY rotate/revoke any exposed credentials" |
|
echo "2. Review and remove secrets from version control history" |
|
echo "3. Implement pre-commit hooks to prevent future leaks" |
|
echo "4. Use environment variables or secret management systems" |
|
echo "5. Conduct security training for development team" |
|
echo "6. Set up continuous monitoring for secret detection" |
|
echo "" |
|
|
|
# Sample findings (first 5) |
|
echo "SAMPLE FINDINGS (first 5):" |
|
echo "-------------------------" |
|
jq -r '.[0:5] | .[] | "File: \(.file)\nDetector: \(.DetectorName)\nRaw: \(.Raw)\n"' "$OUTPUT_FILE" 2>/dev/null |
|
fi |
|
|
|
echo "==========================================" |
|
echo "END OF SUMMARY" |
|
echo "==========================================" |
|
|
|
} > "$SUMMARY_FILE" |
|
|
|
if [ -f "$SUMMARY_FILE" ]; then |
|
SUMMARY_SIZE=$(du -h "$SUMMARY_FILE" | cut -f1) |
|
log_success "Summary report generated: $SUMMARY_FILE ($SUMMARY_SIZE)" |
|
|
|
if [ "$TOTAL_FINDINGS" -gt 0 ]; then |
|
log_warn "Review the summary report for detailed analysis" |
|
else |
|
log_info "Summary report available for reference" |
|
fi |
|
else |
|
log_error "Failed to generate summary report" |
|
fi |
|
} |
|
|
|
# Main execution function |
|
# Orchestrates the entire scan workflow from argument parsing to result processing |
|
# Handles timing and provides comprehensive logging throughout the process |
|
main() { |
|
# Initialize timing |
|
START_TIME=$(date +%s) |
|
|
|
# Parse command line arguments |
|
parse_arguments "$@" |
|
|
|
# Check environment variables |
|
check_environment |
|
|
|
# Validate arguments |
|
validate_arguments |
|
|
|
# Show configuration |
|
log_info "Starting TruffleHog security scan" |
|
log_info "Scan directory: $SCAN_DIR" |
|
log_info "Output file: $OUTPUT_FILE" |
|
log_info "Exclude patterns file: $EXCLUDE_FILE" |
|
log_verbose "Debug mode: $DEBUG" |
|
log_verbose "Verbose mode: $VERBOSE" |
|
log_verbose "Quiet mode: $QUIET" |
|
log_verbose "Colors: $([ "$USE_COLORS" = true ] && echo "enabled" || echo "disabled")" |
|
log_verbose "Verification: $([ "$NO_VERIFICATION" = true ] && echo "disabled" || echo "enabled")" |
|
log_verbose "Summary: $([ "$GENERATE_SUMMARY" = true ] && echo "enabled" || echo "disabled")" |
|
log_verbose "Log level: $LOG_LEVEL" |
|
|
|
# Execute scan workflow |
|
validate_inputs |
|
check_dependencies |
|
prepare_output |
|
run_scan |
|
process_results |
|
|
|
# Generate summary if requested |
|
if [ "$GENERATE_SUMMARY" = true ]; then |
|
generate_summary |
|
else |
|
log_verbose "Summary generation skipped (--no-summary specified)" |
|
fi |
|
|
|
log_info "Scan completed successfully" |
|
} |
|
|
|
# Run main function with all arguments |
|
main "$@" |