mirror of
https://github.com/trufflesecurity/trufflehog.git
synced 2026-06-15 13:24:37 +00:00
131 lines
3.8 KiB
Go
131 lines
3.8 KiB
Go
package git
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
|
)
|
|
|
|
// metricsCollector defines the interface for recording Git scan metrics.
|
|
type metricsCollector interface {
|
|
// Clone metrics
|
|
RecordCloneOperation(status string, reason string, exitCode int)
|
|
|
|
// Scan metrics
|
|
RecordCommitScanned()
|
|
RecordRepoScanned(status string)
|
|
}
|
|
|
|
// Predefined status values
|
|
const (
|
|
statusSuccess = "success"
|
|
statusFailure = "failure"
|
|
)
|
|
|
|
// Predefined clone success reason
|
|
const (
|
|
cloneSuccess = "success"
|
|
)
|
|
|
|
// Predefined clone failure reasons to avoid high cardinality
|
|
const (
|
|
// Authentication/redirection errors
|
|
cloneFailureAuth = "auth_error"
|
|
|
|
// Rate limiting errors
|
|
cloneFailureRateLimit = "rate_limit"
|
|
|
|
// Permission errors
|
|
cloneFailurePermission = "permission_denied"
|
|
|
|
// Network/connection errors
|
|
cloneFailureNetwork = "network_error"
|
|
|
|
// Git reference errors
|
|
cloneFailureReference = "reference_error"
|
|
|
|
// Other/unknown errors
|
|
cloneFailureOther = "other_error"
|
|
)
|
|
|
|
type collector struct {
|
|
cloneOperations *prometheus.CounterVec
|
|
commitsScanned prometheus.Counter
|
|
reposScanned *prometheus.CounterVec
|
|
}
|
|
|
|
var metricsInstance metricsCollector
|
|
|
|
func init() {
|
|
// These are package-level metrics that are incremented by all git scans across the lifetime of the process.
|
|
metricsInstance = &collector{
|
|
cloneOperations: promauto.NewCounterVec(prometheus.CounterOpts{
|
|
Namespace: common.MetricsNamespace,
|
|
Subsystem: common.MetricsSubsystem,
|
|
Name: "git_clone_operations_total",
|
|
Help: "Total number of git clone operations by status, reason, and exit code",
|
|
}, []string{"status", "reason", "exit_code"}),
|
|
|
|
commitsScanned: promauto.NewCounter(prometheus.CounterOpts{
|
|
Namespace: common.MetricsNamespace,
|
|
Subsystem: common.MetricsSubsystem,
|
|
Name: "git_commits_scanned_total",
|
|
Help: "Total number of git commits scanned",
|
|
}),
|
|
|
|
reposScanned: promauto.NewCounterVec(prometheus.CounterOpts{
|
|
Namespace: common.MetricsNamespace,
|
|
Subsystem: common.MetricsSubsystem,
|
|
Name: "git_repos_scanned_total",
|
|
Help: "Total number of git repositories scanned by status (success/failure)",
|
|
}, []string{"status"}),
|
|
}
|
|
}
|
|
|
|
func (c *collector) RecordCloneOperation(status string, reason string, exitCode int) {
|
|
c.cloneOperations.WithLabelValues(status, reason, fmt.Sprintf("%d", exitCode)).Inc()
|
|
}
|
|
|
|
func (c *collector) RecordCommitScanned() {
|
|
c.commitsScanned.Inc()
|
|
}
|
|
|
|
func (c *collector) RecordRepoScanned(status string) {
|
|
c.reposScanned.WithLabelValues(status).Inc()
|
|
}
|
|
|
|
// ClassifyCloneError analyzes the error message and returns the appropriate failure reason
|
|
func ClassifyCloneError(errMsg string) string {
|
|
switch {
|
|
case strings.Contains(errMsg, "unable to update url base from redirection") &&
|
|
strings.Contains(errMsg, "redirect:") && strings.Contains(errMsg, "users/sign_in"):
|
|
return cloneFailureAuth
|
|
|
|
case strings.Contains(errMsg, "The requested URL returned error: 429") ||
|
|
strings.Contains(errMsg, "remote: Retry later"):
|
|
return cloneFailureRateLimit
|
|
|
|
case strings.Contains(errMsg, "The requested URL returned error: 403") ||
|
|
strings.Contains(errMsg, "remote: You are not allowed to download code from this project"):
|
|
return cloneFailurePermission
|
|
|
|
case strings.Contains(errMsg, "RPC failed") ||
|
|
strings.Contains(errMsg, "unexpected disconnect") ||
|
|
strings.Contains(errMsg, "early EOF") ||
|
|
strings.Contains(errMsg, "Problem (3) in the Chunked-Encoded data"):
|
|
return cloneFailureNetwork
|
|
|
|
case strings.Contains(errMsg, "cannot process") ||
|
|
strings.Contains(errMsg, "multiple updates for ref") ||
|
|
strings.Contains(errMsg, "invalid index-pack output"):
|
|
return cloneFailureReference
|
|
|
|
default:
|
|
return cloneFailureOther
|
|
}
|
|
}
|