[analyze] Separate SID from token in twilio analyzer (#3177)

* [analyze] Separate SID from token in twilio analyzer

* Fix test

* Set sid in detector
This commit is contained in:
Miccah 2024-08-05 17:46:57 -07:00 committed by GitHub
parent 59fccbcf3f
commit 1df83f79ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 54 additions and 47 deletions

View file

@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers" "github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers"
@ -16,7 +15,9 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/context" "github.com/trufflesecurity/trufflehog/v3/pkg/context"
) )
type Analyzer struct{} type Analyzer struct {
Cfg *config.Config
}
func (a *Analyzer) Type() analyzerpb.AnalyzerType { func (a *Analyzer) Type() analyzerpb.AnalyzerType {
return analyzerpb.AnalyzerType_Twilio return analyzerpb.AnalyzerType_Twilio
@ -28,14 +29,22 @@ func (a *Analyzer) Analyze(ctx context.Context, credentialInfo map[string]string
return nil, errors.New("key not found in credentialInfo") return nil, errors.New("key not found in credentialInfo")
} }
cfg := &config.Config{} // You might need to adjust this based on how you want to handle config sid, ok := credentialInfo["sid"]
info, err := AnalyzePermissions(cfg, key) if !ok {
return nil, errors.New("sid not found in credentialInfo")
}
if a.Cfg == nil {
a.Cfg = &config.Config{} // You might need to adjust this based on how you want to handle config
}
info, err := AnalyzePermissions(a.Cfg, sid, key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// List parent and subaccounts // List parent and subaccounts
accounts, err := listTwilioAccounts(cfg, key) accounts, err := listTwilioAccounts(a.Cfg, sid, key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -124,15 +133,6 @@ const (
INVALID_CREDENTIALS = 20003 INVALID_CREDENTIALS = 20003
) )
// splitKey splits the key into SID and Secret
func splitKey(key string) (string, string, error) {
split := strings.Split(key, ":")
if len(split) != 2 {
return "", "", errors.New("key must be in the format SID:Secret")
}
return split[0], split[1], nil
}
// getAccountsStatusCode returns the status code from the Accounts endpoint // getAccountsStatusCode returns the status code from the Accounts endpoint
// this is used to determine whether the key is scoped as main or standard, since standard has no access here. // this is used to determine whether the key is scoped as main or standard, since standard has no access here.
func getAccountsStatusCode(cfg *config.Config, sid string, secret string) (int, error) { func getAccountsStatusCode(cfg *config.Config, sid string, secret string) (int, error) {
@ -200,12 +200,7 @@ func getVerifyServicesStatusCode(cfg *config.Config, sid string, secret string)
return serviceRes, nil return serviceRes, nil
} }
func listTwilioAccounts(cfg *config.Config, key string) ([]service, error) { func listTwilioAccounts(cfg *config.Config, sid, secret string) ([]service, error) {
sid, secret, err := splitKey(key)
if err != nil {
return nil, err
}
// create http client // create http client
client := analyzers.NewAnalyzeClient(cfg) client := analyzers.NewAnalyzeClient(cfg)
@ -237,12 +232,7 @@ func listTwilioAccounts(cfg *config.Config, key string) ([]service, error) {
return result.Accounts, nil return result.Accounts, nil
} }
func AnalyzePermissions(cfg *config.Config, key string) (*secretInfo, error) { func AnalyzePermissions(cfg *config.Config, sid, secret string) (*secretInfo, error) {
sid, secret, err := splitKey(key)
if err != nil {
return nil, err
}
servicesRes, err := getVerifyServicesStatusCode(cfg, sid, secret) servicesRes, err := getVerifyServicesStatusCode(cfg, sid, secret)
if err != nil { if err != nil {
return nil, err return nil, err
@ -259,14 +249,8 @@ func AnalyzePermissions(cfg *config.Config, key string) (*secretInfo, error) {
}, nil }, nil
} }
func AnalyzeAndPrintPermissions(cfg *config.Config, key string) { func AnalyzeAndPrintPermissions(cfg *config.Config, sid, secret string) {
// ToDo: Add in logging info, err := AnalyzePermissions(cfg, sid, secret)
if cfg.LoggingEnabled {
color.Red("[x] Logging is not supported for this analyzer.")
return
}
info, err := AnalyzePermissions(cfg, key)
if err != nil { if err != nil {
color.Red("[x] Error: %s", err.Error()) color.Red("[x] Error: %s", err.Error())
return return

View file

@ -20,13 +20,15 @@ func TestAnalyzer_Analyze(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
sid string
key string key string
want string // JSON string want string // JSON string
wantErr bool wantErr bool
}{ }{
{ {
name: "valid Twilio key", name: "valid Twilio key",
key: testSecrets.MustGetField("TWILLIO_ID") + ":" + testSecrets.MustGetField("TWILLIO_API"), sid: testSecrets.MustGetField("TWILLIO_ID"),
key: testSecrets.MustGetField("TWILLIO_API"),
want: ` { want: ` {
"AnalyzerType": 20, "AnalyzerType": 20,
"Bindings": [ "Bindings": [
@ -249,7 +251,7 @@ func TestAnalyzer_Analyze(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
a := Analyzer{} a := Analyzer{}
got, err := a.Analyze(ctx, map[string]string{"key": tt.key}) got, err := a.Analyze(ctx, map[string]string{"key": tt.key, "sid": tt.sid})
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("Analyzer.Analyze() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("Analyzer.Analyze() error = %v, wantErr %v", err, tt.wantErr)
return return

View file

@ -70,7 +70,7 @@ func Run(cmd string) {
case "slack": case "slack":
slack.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"]) slack.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"])
case "twilio": case "twilio":
twilio.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"]) twilio.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["sid"], secretInfo.Parts["key"])
case "airbrake": case "airbrake":
airbrake.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"]) airbrake.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"])
case "huggingface": case "huggingface":

View file

@ -20,19 +20,40 @@ type FormPage struct {
} }
func NewFormPage(c *common.Common, keyType string) FormPage { func NewFormPage(c *common.Common, keyType string) FormPage {
inputs := []textinputs.InputConfig{{ var inputs []textinputs.InputConfig
Label: "Secret", switch keyType {
Key: "key", case "twilio":
Required: true, inputs = []textinputs.InputConfig{{
RedactInput: true, Label: "SID",
}} Key: "sid",
if keyType == "shopify" { Required: true,
inputs = append(inputs, textinputs.InputConfig{ }, {
Label: "Token",
Key: "key",
Required: true,
RedactInput: true,
}}
case "shopify":
inputs = []textinputs.InputConfig{{
Label: "Secret",
Key: "key",
Required: true,
RedactInput: true,
}, {
Label: "Shopify URL", Label: "Shopify URL",
Key: "url", Key: "url",
Required: true, Required: true,
}) }}
default:
inputs = []textinputs.InputConfig{{
Label: "Secret",
Key: "key",
Required: true,
RedactInput: true,
}}
} }
// Always append a log file option.
inputs = append(inputs, textinputs.InputConfig{ inputs = append(inputs, textinputs.InputConfig{
Label: "Log file", Label: "Log file",
Help: "Log HTTP requests that analysis performs to this file", Help: "Log HTTP requests that analysis performs to this file",

View file

@ -90,7 +90,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
if res.StatusCode >= 200 && res.StatusCode < 300 { if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true s1.Verified = true
s1.AnalysisInfo = map[string]string{"key": sid + ":" + key} s1.AnalysisInfo = map[string]string{"key": key, "sid": sid}
var serviceResponse serviceResponse var serviceResponse serviceResponse
if err := json.NewDecoder(res.Body).Decode(&serviceResponse); err == nil && len(serviceResponse.Services) > 0 { // no error in parsing and have at least one service if err := json.NewDecoder(res.Body).Decode(&serviceResponse); err == nil && len(serviceResponse.Services) > 0 { // no error in parsing and have at least one service
service := serviceResponse.Services[0] service := serviceResponse.Services[0]