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
243 lines
5.7 KiB
Go
243 lines
5.7 KiB
Go
//go:generate generate_permissions permissions.yaml permissions.go slack
|
|
|
|
package slack
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"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.AnalyzerTypeSlack }
|
|
|
|
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 *SecretInfo) *analyzers.AnalyzerResult {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
result := analyzers.AnalyzerResult{
|
|
AnalyzerType: analyzers.AnalyzerTypeSlack,
|
|
Metadata: nil,
|
|
}
|
|
|
|
resourceType := "user"
|
|
fullyQualifiedName := info.User.TeamId + "/" + info.User.UserId
|
|
if info.User.BotId != "" {
|
|
resourceType = "bot"
|
|
fullyQualifiedName = info.User.BotId
|
|
}
|
|
resource := analyzers.Resource{
|
|
Name: info.User.User,
|
|
FullyQualifiedName: fullyQualifiedName,
|
|
Type: resourceType,
|
|
Metadata: map[string]any{
|
|
"url": info.User.Url,
|
|
"team": info.User.Team,
|
|
"team_id": info.User.TeamId,
|
|
"scopes": strings.Split(info.Scopes, ","),
|
|
},
|
|
}
|
|
|
|
// extract all permissions
|
|
permissions := extractPermissions(info)
|
|
|
|
result.Bindings = analyzers.BindAllPermissions(resource, permissions...)
|
|
|
|
return &result
|
|
}
|
|
|
|
func extractPermissions(info *SecretInfo) []analyzers.Permission {
|
|
var permissions []analyzers.Permission
|
|
|
|
for _, scope := range strings.Split(info.Scopes, ",") {
|
|
perms, ok := scope_mapping[scope]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
for _, perm := range perms {
|
|
if _, ok := StringToPermission[perm]; !ok {
|
|
// not in out generated permissions,
|
|
continue
|
|
}
|
|
|
|
permissions = append(permissions, analyzers.Permission{
|
|
Value: perm,
|
|
Parent: nil,
|
|
})
|
|
}
|
|
}
|
|
|
|
return permissions
|
|
}
|
|
|
|
// Add in showAll to printScopes + deal with testing enterprise + add scope details
|
|
|
|
type SlackUserData struct {
|
|
Ok bool `json:"ok"`
|
|
Url string `json:"url"`
|
|
Team string `json:"team"`
|
|
User string `json:"user"`
|
|
TeamId string `json:"team_id"`
|
|
UserId string `json:"user_id"`
|
|
BotId string `json:"bot_id"`
|
|
IsEnterprise bool `json:"is_enterprise"`
|
|
}
|
|
|
|
type SecretInfo struct {
|
|
Scopes string
|
|
User SlackUserData
|
|
}
|
|
|
|
func getSlackOAuthScopes(cfg *config.Config, key string) (scopes string, userData SlackUserData, err error) {
|
|
userData = SlackUserData{}
|
|
scopes = ""
|
|
|
|
// URL to which the request will be sent
|
|
url := "https://slack.com/api/auth.test"
|
|
|
|
// Create a client to send the request
|
|
client := analyzers.NewAnalyzeClient(cfg)
|
|
|
|
// Create the request
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return scopes, userData, err
|
|
}
|
|
|
|
// Add the Authorization header to the request
|
|
req.Header.Add("Authorization", "Bearer "+key)
|
|
|
|
// Send the request
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return scopes, userData, err
|
|
}
|
|
defer resp.Body.Close() // Close the response body when the function returns
|
|
|
|
// print body
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return scopes, userData, err
|
|
}
|
|
|
|
// Unmarshal the response body into the SlackUserData struct
|
|
if err := json.Unmarshal(body, &userData); err != nil {
|
|
return scopes, userData, err
|
|
}
|
|
|
|
// Print all headers received from the server
|
|
scopes = resp.Header.Get("X-Oauth-Scopes")
|
|
return scopes, userData, err
|
|
}
|
|
|
|
func AnalyzeAndPrintPermissions(cfg *config.Config, key string) {
|
|
info, err := AnalyzePermissions(cfg, key)
|
|
if err != nil {
|
|
color.Red("[x] Error: %v", err)
|
|
return
|
|
}
|
|
|
|
color.Green("[!] Valid Slack API Key\n\n")
|
|
printIdentityInfo(info.User)
|
|
printScopes(strings.Split(info.Scopes, ","))
|
|
}
|
|
|
|
func AnalyzePermissions(cfg *config.Config, key string) (*SecretInfo, error) {
|
|
scopes, userData, err := getSlackOAuthScopes(cfg, key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error getting Slack OAuth scopes: %w", err)
|
|
}
|
|
|
|
if !userData.Ok {
|
|
return nil, fmt.Errorf("invalid Slack token")
|
|
}
|
|
|
|
return &SecretInfo{
|
|
Scopes: scopes,
|
|
User: userData,
|
|
}, nil
|
|
}
|
|
|
|
func printIdentityInfo(userData SlackUserData) {
|
|
if userData.Url != "" {
|
|
color.Green("URL: %v", userData.Url)
|
|
}
|
|
if userData.Team != "" {
|
|
color.Green("Team: %v", userData.Team)
|
|
}
|
|
if userData.User != "" {
|
|
color.Green("User: %v", userData.User)
|
|
}
|
|
if userData.TeamId != "" {
|
|
color.Green("Team ID: %v", userData.TeamId)
|
|
}
|
|
if userData.UserId != "" {
|
|
color.Green("User ID: %v", userData.UserId)
|
|
}
|
|
if userData.BotId != "" {
|
|
color.Green("Bot ID: %v", userData.BotId)
|
|
}
|
|
fmt.Println("")
|
|
if userData.IsEnterprise {
|
|
color.Green("[!] Slack is Enterprise")
|
|
} else {
|
|
color.Yellow("[-] Slack is not Enterprise")
|
|
}
|
|
fmt.Println("")
|
|
}
|
|
|
|
func printScopes(scopes []string) {
|
|
t := table.NewWriter()
|
|
// if !showAll {
|
|
// t.SetOutputMirror(os.Stdout)
|
|
// t.AppendHeader(table.Row{"Scopes"})
|
|
// for _, scope := range scopes {
|
|
// t.AppendRow([]interface{}{color.GreenString(scope)})
|
|
// }
|
|
// } else {
|
|
t.SetOutputMirror(os.Stdout)
|
|
t.AppendHeader(table.Row{"Scope", "Permissions"})
|
|
for _, scope := range scopes {
|
|
perms := scope_mapping[scope]
|
|
if perms == nil {
|
|
t.AppendRow([]interface{}{color.GreenString(scope), color.GreenString("")})
|
|
} else {
|
|
t.AppendRow([]interface{}{color.GreenString(scope), color.GreenString(strings.Join(perms, ", "))})
|
|
}
|
|
|
|
}
|
|
//}
|
|
|
|
t.Render()
|
|
|
|
}
|