Files
trufflehog/pkg/analyzer/analyzers/errors.go
John Elliott 761be8877b Add AnalysisError type and wrap all analyzer error paths (#4779)
* Add AnalysisError type and AnalysisErrorInfo interface

Introduce a shared error type that provides structured metadata
(analyzer type, operation, service, resource) for analysis failures.
This allows the scanner to extract context from errors without
depending on concrete types.

* Wrap errors in simple API analyzers with AnalysisError

Batch A: Airbrake, Anthropic, Asana, DigitalOcean, DockerHub,
ElevenLabs, Fastly, Groq, HuggingFace, Mailchimp, Mailgun, Mux,
Netlify, Ngrok, Notion, OpenAI, Opsgenie, Posthog, Postman,
Sendgrid, Sourcegraph.

Wraps credential validation errors with operation
"validate_credentials" and AnalyzePermissions errors with
operation "analyze_permissions".

* Wrap errors in remaining analyzers with AnalysisError (Batches B-E)

Batch B (OAuth/multi-credential): airtableoauth, airtablepat, datadog,
dropbox, figma, launchdarkly, plaid
Batch C (Complex): bitbucket, databricks, github, gitlab, jira, monday,
planetscale, shopify, slack, square, stripe, twilio
Batch D (Database): mysql, postgres (service: Database)
Batch E (PrivateKey): privatekey (service: crypto)

* Use Type().String() and constants for NewAnalysisError calls

Address PR feedback: replace hardcoded analyzer type strings with
a.Type().String() and replace raw operation/service strings with
package-level constants (OperationValidateCredentials,
OperationAnalyzePermissions, ServiceAPI, ServiceConfig, etc.).

* Omit empty resource parenthetical from AnalysisError messages

Conditionally include "(resource: ...)" only when non-empty,
avoiding cluttered messages like "... (resource: ): ..." that
appear for the majority of analyzers that don't set a resource.

* Wrap no-data error path in GitHub analyzer with AnalysisError
2026-04-14 17:18:48 -07:00

70 lines
2.1 KiB
Go

package analyzers
import "fmt"
// Operation constants for AnalysisError.
const (
OperationValidateCredentials = "validate_credentials"
OperationAnalyzePermissions = "analyze_permissions"
)
// Service constants for AnalysisError.
const (
ServiceAPI = "API"
ServiceConfig = "config"
ServiceDatabase = "Database"
ServiceOAuth = "OAuth"
ServiceCrypto = "crypto"
)
// AnalysisErrorInfo is implemented by errors that provide structured context
// about analysis failures. This allows downstream consumers (e.g., the scanner)
// to extract metadata for structured error storage without depending on
// concrete error types.
type AnalysisErrorInfo interface {
error
AnalyzerType() string
Operation() string // "validate_credentials", "authenticate", "analyze_permissions", "connect", "ping"
Service() string // "config", "API", "OAuth", "Database"
Resource() string // account ID, endpoint URL, or other identifier
}
// AnalysisError represents a structured error from an analyzer.
type AnalysisError struct {
analyzerType string
operation string
service string
resource string
err error
}
// NewAnalysisError creates a new AnalysisError.
func NewAnalysisError(analyzerType, operation, service, resource string, err error) *AnalysisError {
return &AnalysisError{
analyzerType: analyzerType,
operation: operation,
service: service,
resource: resource,
err: err,
}
}
func (e *AnalysisError) Error() string {
var resource string
if e.resource != "" {
resource = fmt.Sprintf(" (resource: %s)", e.resource)
}
if e.err != nil {
return fmt.Sprintf("%s analysis failed: %s on %s%s: %v",
e.analyzerType, e.operation, e.service, resource, e.err)
}
return fmt.Sprintf("%s analysis failed: %s on %s%s",
e.analyzerType, e.operation, e.service, resource)
}
func (e *AnalysisError) Unwrap() error { return e.err }
func (e *AnalysisError) AnalyzerType() string { return e.analyzerType }
func (e *AnalysisError) Operation() string { return e.operation }
func (e *AnalysisError) Service() string { return e.service }
func (e *AnalysisError) Resource() string { return e.resource }