[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"
"fmt"
"net/http"
"strings"
"github.com/fatih/color"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers"
@ -16,7 +15,9 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
)
type Analyzer struct{}
type Analyzer struct {
Cfg *config.Config
}
func (a *Analyzer) Type() analyzerpb.AnalyzerType {
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")
}
cfg := &config.Config{} // You might need to adjust this based on how you want to handle config
info, err := AnalyzePermissions(cfg, key)
sid, ok := credentialInfo["sid"]
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 {
return nil, err
}
// List parent and subaccounts
accounts, err := listTwilioAccounts(cfg, key)
accounts, err := listTwilioAccounts(a.Cfg, sid, key)
if err != nil {
return nil, err
}
@ -124,15 +133,6 @@ const (
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
// 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) {
@ -200,12 +200,7 @@ func getVerifyServicesStatusCode(cfg *config.Config, sid string, secret string)
return serviceRes, nil
}
func listTwilioAccounts(cfg *config.Config, key string) ([]service, error) {
sid, secret, err := splitKey(key)
if err != nil {
return nil, err
}
func listTwilioAccounts(cfg *config.Config, sid, secret string) ([]service, error) {
// create http client
client := analyzers.NewAnalyzeClient(cfg)
@ -237,12 +232,7 @@ func listTwilioAccounts(cfg *config.Config, key string) ([]service, error) {
return result.Accounts, nil
}
func AnalyzePermissions(cfg *config.Config, key string) (*secretInfo, error) {
sid, secret, err := splitKey(key)
if err != nil {
return nil, err
}
func AnalyzePermissions(cfg *config.Config, sid, secret string) (*secretInfo, error) {
servicesRes, err := getVerifyServicesStatusCode(cfg, sid, secret)
if err != nil {
return nil, err
@ -259,14 +249,8 @@ func AnalyzePermissions(cfg *config.Config, key string) (*secretInfo, error) {
}, nil
}
func AnalyzeAndPrintPermissions(cfg *config.Config, key string) {
// ToDo: Add in logging
if cfg.LoggingEnabled {
color.Red("[x] Logging is not supported for this analyzer.")
return
}
info, err := AnalyzePermissions(cfg, key)
func AnalyzeAndPrintPermissions(cfg *config.Config, sid, secret string) {
info, err := AnalyzePermissions(cfg, sid, secret)
if err != nil {
color.Red("[x] Error: %s", err.Error())
return

View file

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

View file

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

View file

@ -20,19 +20,40 @@ type FormPage struct {
}
func NewFormPage(c *common.Common, keyType string) FormPage {
inputs := []textinputs.InputConfig{{
Label: "Secret",
Key: "key",
Required: true,
RedactInput: true,
}}
if keyType == "shopify" {
inputs = append(inputs, textinputs.InputConfig{
var inputs []textinputs.InputConfig
switch keyType {
case "twilio":
inputs = []textinputs.InputConfig{{
Label: "SID",
Key: "sid",
Required: true,
}, {
Label: "Token",
Key: "key",
Required: true,
RedactInput: true,
}}
case "shopify":
inputs = []textinputs.InputConfig{{
Label: "Secret",
Key: "key",
Required: true,
RedactInput: true,
}, {
Label: "Shopify URL",
Key: "url",
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{
Label: "Log 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 {
s1.Verified = true
s1.AnalysisInfo = map[string]string{"key": sid + ":" + key}
s1.AnalysisInfo = map[string]string{"key": key, "sid": sid}
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
service := serviceResponse.Services[0]