package analyzers import ( "bytes" "encoding/json" "io" "net/http" "sort" "github.com/fatih/color" "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) type ( Analyzer interface { Type() AnalyzerType Analyze(ctx context.Context, credentialInfo map[string]string) (*AnalyzerResult, error) } AnalyzerType int // AnalyzerResult is the output of analysis. AnalyzerResult struct { AnalyzerType AnalyzerType Bindings []Binding UnboundedResources []Resource Metadata map[string]any } Resource struct { Name string FullyQualifiedName string Type string Metadata map[string]any Parent *Resource } Permission struct { Value string Parent *Permission } Binding struct { Resource Resource Permission Permission Condition string } ) type PermissionType string const ( READ PermissionType = "Read" WRITE PermissionType = "Write" READ_WRITE PermissionType = "Read & Write" NONE PermissionType = "None" ERROR PermissionType = "Error" FullAccess string = "full_access" ) const ( AnalyzerTypeInvalid AnalyzerType = iota AnalyzerTypeAirbrake AnalyzerAnthropic AnalyzerTypeAsana AnalyzerTypeBitbucket AnalyzerTypeDockerHub AnalyzerTypeElevenLabs AnalyzerTypeGitHub AnalyzerTypeGitLab AnalyzerTypeHuggingFace AnalyzerTypeMailchimp AnalyzerTypeMailgun AnalyzerTypeMySQL AnalyzerTypeOpenAI AnalyzerTypeOpsgenie AnalyzerTypePostgres AnalyzerTypePostman AnalyzerTypeSendgrid AnalyzerTypeShopify AnalyzerTypeSlack AnalyzerTypeSourcegraph AnalyzerTypeSquare AnalyzerTypeStripe AnalyzerTypeTwilio AnalyzerTypePrivateKey AnalyzerTypeNotion AnalyzerTypeDigitalOcean AnalyzerTypePlanetScale AnalyzerTypeAirtableOAuth AnalyzerTypeAirtablePat AnalyzerTypeGroq AnalyzerTypeLaunchDarkly AnalyzerTypeFigma AnalyzerTypePlaid AnalyzerTypeNetlify AnalyzerTypeFastly AnalyzerTypeMonday AnalyzerTypeDatadog AnalyzerTypeNgrok AnalyzerTypeMux AnalyzerTypePosthog AnalyzerTypeDropbox AnalyzerTypeDataBricks AnalyzerTypeJira // Add new items here with AnalyzerType prefix ) // analyzerTypeStrings maps the enum to its string representation. var analyzerTypeStrings = map[AnalyzerType]string{ AnalyzerTypeInvalid: "Invalid", AnalyzerTypeAirbrake: "Airbrake", AnalyzerAnthropic: "Anthropic", AnalyzerTypeAsana: "Asana", AnalyzerTypeBitbucket: "Bitbucket", AnalyzerTypeDigitalOcean: "DigitalOcean", AnalyzerTypeDockerHub: "DockerHub", AnalyzerTypeElevenLabs: "ElevenLabs", AnalyzerTypeGitHub: "GitHub", AnalyzerTypeGitLab: "GitLab", AnalyzerTypeHuggingFace: "HuggingFace", AnalyzerTypeMailchimp: "Mailchimp", AnalyzerTypeMailgun: "Mailgun", AnalyzerTypeMySQL: "MySQL", AnalyzerTypeOpenAI: "OpenAI", AnalyzerTypeOpsgenie: "Opsgenie", AnalyzerTypePostgres: "Postgres", AnalyzerTypePostman: "Postman", AnalyzerTypeSendgrid: "Sendgrid", AnalyzerTypeShopify: "Shopify", AnalyzerTypeSlack: "Slack", AnalyzerTypeSourcegraph: "Sourcegraph", AnalyzerTypeSquare: "Square", AnalyzerTypeStripe: "Stripe", AnalyzerTypeTwilio: "Twilio", AnalyzerTypePrivateKey: "PrivateKey", AnalyzerTypeNotion: "Notion", AnalyzerTypePlanetScale: "PlanetScale", AnalyzerTypeAirtableOAuth: "AirtableOAuth", AnalyzerTypeAirtablePat: "AirtablePat", AnalyzerTypeGroq: "Groq", AnalyzerTypeLaunchDarkly: "LaunchDarkly", AnalyzerTypeFigma: "Figma", AnalyzerTypePlaid: "Plaid", AnalyzerTypeNetlify: "Netlify", AnalyzerTypeFastly: "Fastly", AnalyzerTypeMonday: "Monday", AnalyzerTypeDatadog: "Datadog", AnalyzerTypeNgrok: "Ngrok", AnalyzerTypeMux: "Mux", AnalyzerTypePosthog: "Posthog", AnalyzerTypeDropbox: "Dropbox", AnalyzerTypeDataBricks: "DataBricks", AnalyzerTypeJira: "Jira", // Add new mappings here } // String method to get the string representation of an AnalyzerType. func (a AnalyzerType) String() string { if str, ok := analyzerTypeStrings[a]; ok { return str } return "Unknown" } // AvailableAnalyzers returns a sorted slice of AnalyzerType strings, skipping "Invalid". func AvailableAnalyzers() []string { var analyzerStrings []string // Iterate through the map to collect all string values except "Invalid". for typ, str := range analyzerTypeStrings { if typ != AnalyzerTypeInvalid { analyzerStrings = append(analyzerStrings, str) } } // Sort the slice alphabetically. sort.Strings(analyzerStrings) return analyzerStrings } type PermissionStatus struct { Value bool IsError bool } type HttpStatusTest struct { URL string Method string Payload map[string]interface{} Params map[string]string Valid []int Invalid []int Type PermissionType Status PermissionStatus Risk string } func (h *HttpStatusTest) RunTest(headers map[string]string) error { // If body data, marshal to JSON var data io.Reader if h.Payload != nil { jsonData, err := json.Marshal(h.Payload) if err != nil { return err } data = bytes.NewBuffer(jsonData) } // Create new HTTP request client := &http.Client{} req, err := http.NewRequest(h.Method, h.URL, data) if err != nil { return err } // Add custom headers if provided for key, value := range headers { req.Header.Set(key, value) } // Execute HTTP Request resp, err := client.Do(req) if err != nil { return err } defer func() { _ = resp.Body.Close() }() // Check response status code switch { case StatusContains(resp.StatusCode, h.Valid): h.Status.Value = true case StatusContains(resp.StatusCode, h.Invalid): h.Status.Value = false default: h.Status.IsError = true } return nil } type Scope struct { Name string Tests []interface{} } func StatusContains(status int, vals []int) bool { for _, v := range vals { if status == v { return true } } return false } func GetWriterFromStatus(status PermissionType) func(a ...interface{}) string { switch status { case READ: return color.New(color.FgYellow).SprintFunc() case WRITE: return color.New(color.FgGreen).SprintFunc() case READ_WRITE: return color.New(color.FgGreen).SprintFunc() case NONE: return color.New().SprintFunc() case ERROR: return color.New(color.FgRed).SprintFunc() default: return color.New().SprintFunc() } } var GreenWriter = color.New(color.FgGreen).SprintFunc() var YellowWriter = color.New(color.FgYellow).SprintFunc() var RedWriter = color.New(color.FgRed).SprintFunc() var DefaultWriter = color.New().SprintFunc() // BindAllPermissions creates a Binding for each permission to the given // resource. func BindAllPermissions(r Resource, perms ...Permission) []Binding { bindings := make([]Binding, len(perms)) for i, perm := range perms { bindings[i] = Binding{ Resource: r, Permission: perm, } } return bindings }