Lint / golangci-lint (push) Waiting to run
Lint / semgrep (push) Waiting to run
Release / Release (push) Waiting to run
Scan for secrets / test (push) Waiting to run
Snifftest / Run Snifftest (push) Waiting to run
Test / test (push) Waiting to run
Test / test-community (push) Waiting to run
Only used for tracking analyzer types
171 lines
4.2 KiB
Go
171 lines
4.2 KiB
Go
//go:generate generate_permissions permissions.yaml permissions.go mailgun
|
|
package mailgun
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/jedib0t/go-pretty/table"
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers"
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/config"
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
|
)
|
|
|
|
var _ analyzers.Analyzer = (*Analyzer)(nil)
|
|
|
|
type Analyzer struct {
|
|
Cfg *config.Config
|
|
}
|
|
|
|
func (Analyzer) Type() analyzers.AnalyzerType { return analyzers.AnalyzerTypeMailgun }
|
|
|
|
func (a Analyzer) Analyze(_ context.Context, credInfo map[string]string) (*analyzers.AnalyzerResult, error) {
|
|
key, ok := credInfo["key"]
|
|
if !ok {
|
|
return nil, errors.New("key not found in credentialInfo")
|
|
}
|
|
|
|
info, err := AnalyzePermissions(a.Cfg, key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return secretInfoToAnalyzerResult(info), nil
|
|
}
|
|
|
|
func secretInfoToAnalyzerResult(info *DomainsJSON) *analyzers.AnalyzerResult {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
result := analyzers.AnalyzerResult{
|
|
AnalyzerType: analyzers.AnalyzerTypeMailgun,
|
|
Bindings: make([]analyzers.Binding, len(info.Items)),
|
|
}
|
|
|
|
for idx, domain := range info.Items {
|
|
result.Bindings[idx] = analyzers.Binding{
|
|
Resource: analyzers.Resource{
|
|
Name: domain.URL,
|
|
FullyQualifiedName: "mailgun/" + domain.ID + "/" + domain.URL,
|
|
Type: "domain",
|
|
Metadata: map[string]any{
|
|
"created_at": domain.CreatedAt,
|
|
"type": domain.Type,
|
|
"state": domain.State,
|
|
"is_disabled": domain.IsDisabled,
|
|
},
|
|
},
|
|
Permission: analyzers.Permission{
|
|
Value: PermissionStrings[FullAccess],
|
|
},
|
|
}
|
|
}
|
|
return &result
|
|
}
|
|
|
|
type Domain struct {
|
|
ID string `json:"id"`
|
|
URL string `json:"name"`
|
|
IsDisabled bool `json:"is_disabled"`
|
|
Type string `json:"type"`
|
|
State string `json:"state"`
|
|
CreatedAt string `json:"created_at"`
|
|
}
|
|
|
|
type DomainsJSON struct {
|
|
Items []Domain `json:"items"`
|
|
TotalCount int `json:"total_count"`
|
|
}
|
|
|
|
func getDomains(cfg *config.Config, apiKey string) (DomainsJSON, int, error) {
|
|
var domainsJSON DomainsJSON
|
|
|
|
client := analyzers.NewAnalyzeClient(cfg)
|
|
req, err := http.NewRequest("GET", "https://api.mailgun.net/v4/domains", nil)
|
|
if err != nil {
|
|
return domainsJSON, -1, err
|
|
}
|
|
|
|
req.SetBasicAuth("api", apiKey)
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return domainsJSON, -1, err
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
return domainsJSON, resp.StatusCode, nil
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
err = json.NewDecoder(resp.Body).Decode(&domainsJSON)
|
|
if err != nil {
|
|
return domainsJSON, resp.StatusCode, err
|
|
}
|
|
return domainsJSON, resp.StatusCode, nil
|
|
}
|
|
|
|
func AnalyzeAndPrintPermissions(cfg *config.Config, apiKey string) {
|
|
data, err := AnalyzePermissions(cfg, apiKey)
|
|
if err != nil {
|
|
color.Red("[x] %s", err.Error())
|
|
return
|
|
}
|
|
|
|
printMetadata(data)
|
|
}
|
|
|
|
func AnalyzePermissions(cfg *config.Config, apiKey string) (*DomainsJSON, error) {
|
|
// Get the domains associated with the API key
|
|
domains, statusCode, err := getDomains(cfg, apiKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error getting domains: %s", err)
|
|
}
|
|
|
|
if statusCode != 200 {
|
|
return nil, fmt.Errorf("Invalid Mailgun API key.")
|
|
}
|
|
color.Green("[i] Valid Mailgun API key\n\n")
|
|
color.Green("[i] Permissions: Full Access\n\n")
|
|
|
|
return &domains, nil
|
|
}
|
|
|
|
func printMetadata(domains *DomainsJSON) {
|
|
if domains.TotalCount == 0 {
|
|
color.Red("[i] No domains found")
|
|
return
|
|
}
|
|
color.Yellow("[i] Found %d domain(s)", domains.TotalCount)
|
|
|
|
t := table.NewWriter()
|
|
t.SetOutputMirror(os.Stdout)
|
|
t.AppendHeader(table.Row{"Domain", "Type", "State", "Created At", "Disabled"})
|
|
|
|
for _, domain := range domains.Items {
|
|
|
|
var colorFunc func(format string, a ...interface{}) string
|
|
switch {
|
|
case domain.IsDisabled:
|
|
colorFunc = color.RedString
|
|
case domain.Type == "sandbox" || domain.State == "unverified":
|
|
colorFunc = color.YellowString
|
|
default:
|
|
colorFunc = color.GreenString
|
|
}
|
|
|
|
t.AppendRow([]interface{}{
|
|
colorFunc(domain.URL),
|
|
colorFunc(domain.Type),
|
|
colorFunc(domain.State),
|
|
colorFunc(domain.CreatedAt),
|
|
colorFunc(strconv.FormatBool(domain.IsDisabled)),
|
|
})
|
|
}
|
|
t.Render()
|
|
}
|