package aws

import (
	"fmt"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	"github.com/BishopFox/cloudfox/aws/sdk"
	"github.com/BishopFox/cloudfox/internal"
	"github.com/BishopFox/cloudfox/internal/aws/policy"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/iam"
	"github.com/aws/aws-sdk-go-v2/service/sts"
	"github.com/bishopfox/knownawsaccountslookup"
	"github.com/sirupsen/logrus"
)

type RoleTrustsModule struct {
	// General configuration data
	IAMClient                        sdk.AWSIAMClientInterface
	IAMSimulatePrincipalPolicyClient iam.SimulatePrincipalPolicyAPIClient

	Caller         sts.GetCallerIdentityOutput
	AWSProfile     string
	Goroutines     int
	CommandCounter internal.CommandCounter
	SkipAdminCheck bool
	WrapTable      bool
	AWSOutputType  string
	AWSTableCols   string

	pmapperMod          PmapperModule
	pmapperError        error
	PmapperDataBasePath string

	iamSimClient IamSimulatorModule

	// Main module data
	AnalyzedRoles  []AnalyzedRole
	RoleTrustTable []RoleTrustRow

	vendors *knownawsaccountslookup.Vendors

	// Used to store output data for pretty printing
	output internal.OutputData2

	modLog *logrus.Entry
}

type RoleTrustRow struct {
	RoleARN                  string
	RoleName                 string
	TrustedPrincipal         string
	TrustedService           string
	TrustedFederatedProvider string
	TrustedFederatedSubject  string
	ExternalID               string
	IsAdmin                  string
	CanPrivEsc               string
}

type AnalyzedRole struct {
	roleARN   *string
	trustsDoc policy.TrustPolicyDocument
	// trustType  string // UNUSED FIELD, PLEASE REVIEW
	Admin      string
	CanPrivEsc string
}

func (m *RoleTrustsModule) PrintRoleTrusts(outputDirectory string, verbosity int) {
	m.output.Verbosity = verbosity
	m.output.Directory = outputDirectory
	m.output.CallingModule = "role-trusts"
	m.modLog = internal.TxtLog.WithFields(logrus.Fields{
		"module": m.output.CallingModule,
	})
	if m.AWSProfile == "" {
		m.AWSProfile = internal.BuildAWSPath(m.Caller)
	}
	localAdminMap := make(map[string]bool)
	m.vendors = knownawsaccountslookup.NewVendorMap()
	m.vendors.PopulateKnownAWSAccounts()

	fmt.Printf("[%s][%s] Enumerating role trusts for account %s.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), aws.ToString(m.Caller.Account))
	//fmt.Printf("[%s][%s] Looking for pmapper data for this account and building a PrivEsc graph in golang if it exists.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile))
	m.pmapperMod, m.pmapperError = InitPmapperGraph(m.Caller, m.AWSProfile, m.Goroutines, m.PmapperDataBasePath)
	m.iamSimClient = InitIamCommandClient(m.IAMClient, m.Caller, m.AWSProfile, m.Goroutines)
	// if m.pmapperError != nil {
	// 	fmt.Printf("[%s][%s] No pmapper data found for this account. Using cloudfox's iam-simulator for role analysis\n", cyan(m.output.CallingModule), cyan(m.AWSProfile))
	// } else {
	// 	fmt.Printf("[%s][%s] Found pmapper data for this account. Using it for role analysis\n", cyan(m.output.CallingModule), cyan(m.AWSProfile))
	// }
	m.getAllRoleTrusts()

	if m.pmapperError == nil {
		for i := range m.AnalyzedRoles {
			m.AnalyzedRoles[i].Admin, m.AnalyzedRoles[i].CanPrivEsc = GetPmapperResults(m.SkipAdminCheck, m.pmapperMod, m.AnalyzedRoles[i].roleARN)
		}
	} else {
		for i := range m.AnalyzedRoles {
			m.AnalyzedRoles[i].Admin, m.AnalyzedRoles[i].CanPrivEsc = GetIamSimResult(m.SkipAdminCheck, m.AnalyzedRoles[i].roleARN, m.iamSimClient, localAdminMap)

		}
	}

	o := internal.OutputClient{
		Verbosity:     verbosity,
		CallingModule: m.output.CallingModule,
		Table: internal.TableClient{
			Wrap: m.WrapTable,
		},
	}

	o.PrefixIdentifier = m.AWSProfile
	o.Table.DirectoryName = filepath.Join(outputDirectory, "cloudfox-output", "aws", fmt.Sprintf("%s-%s", m.AWSProfile, aws.ToString(m.Caller.Account)))

	principalsHeader, principalsBody, principalTableCols := m.printPrincipalTrusts(outputDirectory)
	o.Table.TableFiles = append(o.Table.TableFiles, internal.TableFile{
		Header:    principalsHeader,
		Body:      principalsBody,
		TableCols: principalTableCols,
		Name:      "role-trusts-principals",
	})
	rootPrincipalsHeader, rootPrincipalsBody, rootPrincipalTableCols := m.printPrincipalTrustsRootOnly(outputDirectory)
	o.Table.TableFiles = append(o.Table.TableFiles, internal.TableFile{
		Header:            rootPrincipalsHeader,
		Body:              rootPrincipalsBody,
		TableCols:         rootPrincipalTableCols,
		Name:              "role-trusts-principals-root-trusts-without-external-id",
		SkipPrintToScreen: true,
	})
	servicesHeader, servicesBody, serviceTableCols := m.printServiceTrusts(outputDirectory)
	o.Table.TableFiles = append(o.Table.TableFiles, internal.TableFile{
		Header:    servicesHeader,
		Body:      servicesBody,
		TableCols: serviceTableCols,
		Name:      "role-trusts-services",
	})

	federatedHeader, federatedBody, federatedTableCols := m.printFederatedTrusts(outputDirectory)
	o.Table.TableFiles = append(o.Table.TableFiles, internal.TableFile{
		Header:    federatedHeader,
		Body:      federatedBody,
		TableCols: federatedTableCols,
		Name:      "role-trusts-federated",
	})

	o.WriteFullOutput(o.Table.TableFiles, nil)
	if len(principalsBody) > 0 {
		fmt.Printf("[%s][%s] %s principal role trusts found.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), strconv.Itoa(len(principalsBody)))
	} else {
		fmt.Printf("[%s][%s] No role trusts found, skipping the creation of an output file.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile))
	}
	if len(servicesBody) > 0 {
		fmt.Printf("[%s][%s] %s service role trusts found.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), strconv.Itoa(len(servicesBody)))
	} else {
		fmt.Printf("[%s][%s] No role trusts found, skipping the creation of an output file.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile))
	}
	if len(federatedBody) > 0 {
		fmt.Printf("[%s][%s] %s federated role trusts found.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), strconv.Itoa(len(federatedBody)))
	} else {
		fmt.Printf("[%s][%s] No role trusts found, skipping the creation of an output file.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile))
	}

}

func (m *RoleTrustsModule) printPrincipalTrusts(outputDirectory string) ([]string, [][]string, []string) {
	var header []string
	var body [][]string
	m.output.FullFilename = ""
	m.output.Body = nil
	m.output.CallingModule = "role-trusts"
	m.output.FullFilename = "role-trusts-principals"
	header = []string{
		"Account",
		"Role Arn",
		"Role Name",
		"Trusted Principal",
		"ExternalID",
		"IsAdmin?",
		"CanPrivEscToAdmin?",
	}

	// If the user specified table columns, use those.
	// If the user specified -o wide, use the wide default cols for this module.
	// Otherwise, use the hardcoded default cols for this module.
	var tableCols []string
	// If the user specified table columns, use those.
	if m.AWSTableCols != "" {
		tableCols = strings.Split(m.AWSTableCols, ",")
		// If the user specified wide as the output format, use these columns.
	} else if m.AWSOutputType == "wide" {
		tableCols = []string{"Account", "Role Arn", "Trusted Principal", "ExternalID", "IsAdmin?", "CanPrivEscToAdmin?"}
		// Otherwise, use the default columns for this module (brief)
	} else {
		tableCols = []string{"Role Name", "Trusted Principal", "ExternalID", "IsAdmin?", "CanPrivEscToAdmin?"}
	}
	// Remove the pmapper row if there is no pmapper data
	if m.pmapperError != nil {
		sharedLogger.Errorf("%s - %s - No pmapper data found for this account. Skipping the pmapper column in the output table.", m.output.CallingModule, m.AWSProfile)
		tableCols = removeStringFromSlice(tableCols, "CanPrivEscToAdmin?")
	}

	for _, role := range m.AnalyzedRoles {
		for _, statement := range role.trustsDoc.Statement {
			for _, principal := range statement.Principal.AWS {
				//check to see if the accountID is known
				if strings.Contains(principal, "arn:aws:iam::") || strings.Contains(principal, "root") {
					accountID := strings.Split(principal, ":")[4]
					vendorName := m.vendors.GetVendorNameFromAccountID(accountID)
					if vendorName != "" {
						principal = fmt.Sprintf("%s (%s)", principal, vendorName)
					}
				}

				RoleTrustRow := RoleTrustRow{
					RoleARN:          aws.ToString(role.roleARN),
					RoleName:         GetResourceNameFromArn(aws.ToString(role.roleARN)),
					TrustedPrincipal: principal,
					ExternalID:       statement.Condition.StringEquals.StsExternalID,
					IsAdmin:          role.Admin,
					CanPrivEsc:       role.CanPrivEsc,
				}
				body = append(body, []string{
					aws.ToString(m.Caller.Account),
					RoleTrustRow.RoleARN,
					RoleTrustRow.RoleName,
					RoleTrustRow.TrustedPrincipal,
					RoleTrustRow.ExternalID,
					RoleTrustRow.IsAdmin,
					RoleTrustRow.CanPrivEsc})
			}
		}

	}

	m.sortTrustsTablePerTrustedPrincipal()
	return header, body, tableCols

}

// printPrincipalTrusts but only those that have a trusted principal that contains :root and also does not have an external ID
func (m *RoleTrustsModule) printPrincipalTrustsRootOnly(outputDirectory string) ([]string, [][]string, []string) {
	var header []string
	var body [][]string
	m.output.FullFilename = ""
	m.output.Body = nil
	m.output.CallingModule = "role-trusts"
	m.output.FullFilename = "role-trusts-principals-root-trusts-without-external-id"
	header = []string{
		"Account",
		"Role Arn",
		"Role Name",
		"Trusted Principal",
		"ExternalID",
		"IsAdmin?",
		"CanPrivEscToAdmin?",
	}

	// If the user specified table columns, use those.
	// If the user specified -o wide, use the wide default cols for this module.
	// Otherwise, use the hardcoded default cols for this module.
	var tableCols []string
	// If the user specified table columns, use those.
	if m.AWSTableCols != "" {
		tableCols = strings.Split(m.AWSTableCols, ",")
		// If the user specified wide as the output format, use these columns.
	} else if m.AWSOutputType == "wide" {
		tableCols = []string{"Account", "Role Arn", "Trusted Principal", "ExternalID", "IsAdmin?", "CanPrivEscToAdmin?"}
		// Otherwise, use the default columns for this module (brief)
	} else {
		tableCols = []string{"Role Name", "Trusted Principal", "ExternalID", "IsAdmin?", "CanPrivEscToAdmin?"}
	}
	// Remove the pmapper row if there is no pmapper data
	if m.pmapperError != nil {
		sharedLogger.Errorf("%s - %s - No pmapper data found for this account. Skipping the pmapper column in the output table.", m.output.CallingModule, m.AWSProfile)
		tableCols = removeStringFromSlice(tableCols, "CanPrivEscToAdmin?")
	}
	for _, role := range m.AnalyzedRoles {
		for _, statement := range role.trustsDoc.Statement {
			for _, principal := range statement.Principal.AWS {
				if strings.Contains(principal, ":root") && statement.Condition.StringEquals.StsExternalID == "" {
					accountID := strings.Split(principal, ":")[4]
					vendorName := m.vendors.GetVendorNameFromAccountID(accountID)
					if vendorName != "" {
						principal = fmt.Sprintf("%s (%s)", principal, vendorName)
					}

					RoleTrustRow := RoleTrustRow{
						RoleARN:          aws.ToString(role.roleARN),
						RoleName:         GetResourceNameFromArn(aws.ToString(role.roleARN)),
						TrustedPrincipal: principal,
						ExternalID:       statement.Condition.StringEquals.StsExternalID,
						IsAdmin:          role.Admin,
						CanPrivEsc:       role.CanPrivEsc,
					}
					body = append(body, []string{
						aws.ToString(m.Caller.Account),
						RoleTrustRow.RoleARN,
						RoleTrustRow.RoleName,
						RoleTrustRow.TrustedPrincipal,
						RoleTrustRow.ExternalID,
						RoleTrustRow.IsAdmin,
						RoleTrustRow.CanPrivEsc})
				}
			}
		}
	}

	m.sortTrustsTablePerTrustedPrincipal()
	return header, body, tableCols
}

func (m *RoleTrustsModule) printServiceTrusts(outputDirectory string) ([]string, [][]string, []string) {
	var header []string
	var body [][]string
	m.output.FullFilename = ""
	m.output.Body = nil
	m.output.CallingModule = "role-trusts"
	m.output.FullFilename = "role-trusts-services"
	header = []string{
		"Account",
		"Role Arn",
		"Role Name",
		"Trusted Service",
		"IsAdmin?",
		"CanPrivEscToAdmin?",
	}

	// If the user specified table columns, use those.
	// If the user specified -o wide, use the wide default cols for this module.
	// Otherwise, use the hardcoded default cols for this module.
	var tableCols []string
	// If the user specified table columns, use those.
	if m.AWSTableCols != "" {
		tableCols = strings.Split(m.AWSTableCols, ",")
		// If the user specified wide as the output format, use these columns.
	} else if m.AWSOutputType == "wide" {
		tableCols = []string{"Account", "Role Arn", "Trusted Service", "IsAdmin?", "CanPrivEscToAdmin?"}
		// Otherwise, use the default columns for this module (brief)
	} else {
		tableCols = []string{"Role Name", "Trusted Service", "IsAdmin?", "CanPrivEscToAdmin?"}
	}
	// Remove the pmapper row if there is no pmapper data
	if m.pmapperError != nil {
		sharedLogger.Errorf("%s - %s - No pmapper data found for this account. Skipping the pmapper column in the output table.", m.output.CallingModule, m.AWSProfile)
		tableCols = removeStringFromSlice(tableCols, "CanPrivEscToAdmin?")
	}

	for _, role := range m.AnalyzedRoles {
		for _, statement := range role.trustsDoc.Statement {
			for _, service := range statement.Principal.Service {
				RoleTrustRow := RoleTrustRow{
					RoleARN:        aws.ToString(role.roleARN),
					RoleName:       GetResourceNameFromArn(aws.ToString(role.roleARN)),
					TrustedService: service,
					IsAdmin:        role.Admin,
					CanPrivEsc:     role.CanPrivEsc,
				}
				body = append(body, []string{
					aws.ToString(m.Caller.Account),
					RoleTrustRow.RoleARN,
					RoleTrustRow.RoleName,
					RoleTrustRow.TrustedService,
					RoleTrustRow.IsAdmin,
					RoleTrustRow.CanPrivEsc})

			}
		}
	}

	// sort the rows based on column 2 (service)
	sort.SliceStable(body, func(i, j int) bool {
		return body[i][3] < body[j][3]
	})

	return header, body, tableCols

}

func (m *RoleTrustsModule) printFederatedTrusts(outputDirectory string) ([]string, [][]string, []string) {
	var header []string
	var body [][]string
	m.output.FullFilename = ""
	m.output.Body = nil
	m.output.CallingModule = "role-trusts"
	m.output.FullFilename = "role-trusts-federated"
	header = []string{
		"Account",
		"Role Arn",
		"Role Name",
		"Trusted Provider",
		"Trusted Subject",
		"IsAdmin?",
		"CanPrivEscToAdmin?",
	}

	// If the user specified table columns, use those.
	// If the user specified -o wide, use the wide default cols for this module.
	// Otherwise, use the hardcoded default cols for this module.
	var tableCols []string
	// If the user specified table columns, use those.
	if m.AWSTableCols != "" {
		tableCols = strings.Split(m.AWSTableCols, ",")
		// If the user specified wide as the output format, use these columns.
	} else if m.AWSOutputType == "wide" {
		tableCols = []string{"Account", "Role Arn", "Trusted Provider", "Trusted Subject", "IsAdmin?", "CanPrivEscToAdmin?"}
		// Otherwise, use the default columns for this module (brief)
	} else {
		tableCols = []string{"Role Name", "Trusted Provider", "Trusted Subject", "IsAdmin?", "CanPrivEscToAdmin?"}
	}
	// Remove the pmapper row if there is no pmapper data
	if m.pmapperError != nil {
		sharedLogger.Errorf("%s - %s - No pmapper data found for this account. Skipping the pmapper column in the output table.", m.output.CallingModule, m.AWSProfile)
		tableCols = removeStringFromSlice(tableCols, "CanPrivEscToAdmin?")
	}
	for _, role := range m.AnalyzedRoles {
		for _, statement := range role.trustsDoc.Statement {
			if len(statement.Principal.Federated) > 0 {
				provider, subjects := parseFederatedTrustPolicy(statement)
				for _, subject := range subjects {
					RoleTrustRow := RoleTrustRow{
						RoleARN:                  aws.ToString(role.roleARN),
						RoleName:                 GetResourceNameFromArn(aws.ToString(role.roleARN)),
						TrustedFederatedProvider: provider,
						TrustedFederatedSubject:  subject,
						IsAdmin:                  role.Admin,
						CanPrivEsc:               role.CanPrivEsc,
					}
					body = append(body, []string{
						aws.ToString(m.Caller.Account),
						RoleTrustRow.RoleARN,
						RoleTrustRow.RoleName,
						RoleTrustRow.TrustedFederatedProvider,
						RoleTrustRow.TrustedFederatedSubject,
						RoleTrustRow.IsAdmin,
						RoleTrustRow.CanPrivEsc})
				}
			}

		}
	}

	m.sortTrustsTablePerTrustedPrincipal()
	return header, body, tableCols

}

func parseFederatedTrustPolicy(statement policy.RoleTrustStatementEntry) (string, []string) {
	var provider string
	var subjects []string
	if len(statement.Principal.Federated) > 1 {
		sharedLogger.Warnf("Multiple federated providers found in the trust policy. This is not currently supported. Please review the trust policy for specifics.")
		provider = "Multiple Federated Providers"
		subjects = append(subjects, "Review policy for specifics\nand submit issue to cloudfox repo.")
	}

	switch {
	// lets use the Federated field to determine the provider, then based on the provider we can grab the list of subjects
	case strings.Contains(statement.Principal.Federated[0], "token.actions.githubusercontent.com"):
		provider = "GitHub"
		if len(statement.Condition.StringLike.TokenActionsGithubusercontentComSub) > 0 {
			subjects = append(subjects, statement.Condition.StringLike.TokenActionsGithubusercontentComSub...)
		} else if len(statement.Condition.StringEquals.TokenActionsGithubusercontentComSub) > 0 {
			subjects = append(subjects, statement.Condition.StringEquals.TokenActionsGithubusercontentComSub...)
		} else {
			subjects = append(subjects, "ALL REPOS!!!")
		}
	case strings.Contains(statement.Principal.Federated[0], "oidc.eks"):
		// extract accountId from statement.Principal.Federated[0]
		accountId := strings.Split(statement.Principal.Federated[0], ":")[4]
		provider = fmt.Sprintf("EKS-%s", accountId)
		//provider = "EKS"
		//provider = statement.Principal.Federated[0]
		if len(statement.Condition.StringLike.OidcEksSub) > 0 {
			subjects = append(subjects, statement.Condition.StringLike.OidcEksSub...)
		} else if len(statement.Condition.StringEquals.OidcEksSub) > 0 {
			subjects = append(subjects, statement.Condition.StringEquals.OidcEksSub...)
		} else {
			subjects = append(subjects, "ALL SERVICE ACCOUNTS!!!")
		}
		// terraform case
	case strings.Contains(statement.Principal.Federated[0], "app.terraform.io"):
		provider = "Terraform Cloud"
		if len(statement.Condition.StringLike.TerraformSub) > 0 {
			subjects = append(subjects, statement.Condition.StringLike.TerraformSub...)
		} else if len(statement.Condition.StringEquals.TerraformSub) > 0 {
			subjects = append(subjects, statement.Condition.StringEquals.TerraformSub...)
		} else {
			subjects = append(subjects, "ALL WORKSPACES")
		}
		// Azure AD case
	case strings.Contains(statement.Principal.Federated[0], "http://sts.windows.net"):
		provider = "Azure AD"
		if len(statement.Condition.StringLike.AzureADIss) > 0 {
			subjects = append(subjects, statement.Condition.StringLike.AzureADIss...)
		} else if len(statement.Condition.StringEquals.AzureADIss) > 0 {
			subjects = append(subjects, statement.Condition.StringEquals.AzureADIss...)
		} else {
			subjects = append(subjects, "ALL ISSUERS")
		}

	///AWS SSO case
	case strings.Contains(statement.Principal.Federated[0], "AWSSSO"):
		//provider = "AWS SSO"
		accountId := strings.Split(statement.Principal.Federated[0], ":")[4]
		provider = fmt.Sprintf("AWSSSO-%s", accountId)
		subjects = append(subjects, "Not applicable")

	// okta case
	case strings.Contains(statement.Principal.Federated[0], "Okta"):
		provider = "Okta"
		subjects = append(subjects, "Not applicable")

	// cognito case
	case statement.Principal.Federated[0] == "cognito-identity.amazonaws.com":
		provider = "Cognito"
		if statement.Condition.ForAnyValueStringLike.CognitoAMR != "" {
			subjects = append(subjects, statement.Condition.ForAnyValueStringLike.CognitoAMR)
		} else {
			subjects = append(subjects, "ALL IDENTITIES")
		}
		// google workspace case
	case strings.Contains(statement.Principal.Federated[0], "workspace.google.com"):
		provider = "Google Workspace"
		if len(statement.Condition.StringLike.GoogleWorkspaceSub) > 0 {
			subjects = append(subjects, statement.Condition.StringLike.GoogleWorkspaceSub...)
		} else if len(statement.Condition.StringEquals.GoogleWorkspaceSub) > 0 {
			subjects = append(subjects, statement.Condition.StringEquals.GoogleWorkspaceSub...)
		} else {
			subjects = append(subjects, "ALL USERS")
		}
		// GCP case
	case strings.Contains(statement.Principal.Federated[0], "accounts.google.com"):
		provider = "GCP"
		if len(statement.Condition.StringLike.GCPSub) > 0 {
			subjects = append(subjects, statement.Condition.StringLike.GCPSub...)
		} else if len(statement.Condition.StringEquals.GCPSub) > 0 {
			subjects = append(subjects, statement.Condition.StringEquals.GCPSub...)
		} else {
			subjects = append(subjects, "ALL USERS")
		}
	// auth0 case
	//not ready yet
	// case strings.Contains(statement.Principal.Federated[0], "auth0.com"):
	// 	provider = "Auth0"
	// 	if len(statement.Condition.ForAnyValueStringLike.Auth0Amr) > 0 {
	// 		subjects = append(subjects, statement.Condition.ForAnyValueStringLike.Auth0Amr...)
	// 	} else {
	// 		subjects = append(subjects, "ALL GROUPS")
	// 	}
	// circleci case
	case strings.Contains(statement.Principal.Federated[0], "oidc.circleci.com"):
		provider = "CircleCI"
		if len(statement.Condition.StringLike.CircleCIAud) > 0 {
			subjects = append(subjects, statement.Condition.StringLike.CircleCIAud...)
		} else if len(statement.Condition.StringEquals.CircleCIAud) > 0 {
			subjects = append(subjects, statement.Condition.StringEquals.CircleCIAud...)
		} else {
			subjects = append(subjects, "ALL PROJECTS")
		}
	case strings.Contains(statement.Principal.Federated[0], "saml-provider"):
		// the provider name is the last part of the ARN
		provider = strings.Split(statement.Principal.Federated[0], ":saml-provider/")[1]
		subjects = append(subjects, "Not applicable")

	default:
		provider = "Unknown Federated Provider"
		subjects = append(subjects, "Review policy for specifics\nand submit issue to cloudfox repo.")

	}
	return provider, subjects
}

func (m *RoleTrustsModule) sortTrustsTablePerTrustedPrincipal() {
	sort.Slice(
		m.output.Body,
		func(i int, j int) bool {
			return m.output.Body[i][1] < m.output.Body[j][1]
		},
	)
}

func (m *RoleTrustsModule) getAllRoleTrusts() {
	ListRoles, err := sdk.CachedIamListRoles(m.IAMClient, aws.ToString(m.Caller.Account))
	if err != nil {
		m.modLog.Error(err.Error())
		m.CommandCounter.Error++
	}

	for _, role := range ListRoles {
		trustsdoc, err := policy.ParseRoleTrustPolicyDocument(role)
		if err != nil {
			m.modLog.Error(err.Error())
			m.CommandCounter.Error++
			break
		}

		if role.Arn != nil {
			m.AnalyzedRoles = append(m.AnalyzedRoles, AnalyzedRole{
				roleARN:    role.Arn,
				trustsDoc:  trustsdoc,
				Admin:      "",
				CanPrivEsc: "",
			})

		}

	}

}