diff --git a/README.md b/README.md index b6070219..11d06da0 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ For the full documentation please refer to our [wiki](https://github.com/BishopF | Provider| CloudFox Commands | | - | - | -| AWS | 34 | +| AWS | 35 | | Azure | 4 | | GCP | 60 | | Kubernetes | Support Planned | @@ -160,6 +160,7 @@ For detailed setup instructions, see the [GCP Setup Guide](https://github.com/Bi | AWS | [all-checks](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#all-checks) | Run all of the other commands using reasonable defaults. You'll still want to check out the non-default options of each command, but this is a great place to start. | | AWS | [access-keys](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#access-keys) | Lists active access keys for all users. Useful for cross referencing a key you found with which in-scope account it belongs to. | | AWS | [api-gw](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#api-gw) | Lists API gateway endpoints and gives you custom curl commands including API tokens if they are stored in metadata. | +| AWS | [bedrock-agents](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#bedrock-agents) | Enumerate Bedrock agents, action groups, collaborators, and aliases. Extracts system prompts, Lambda integrations, and API schemas. | | AWS | [buckets](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#filesystems) | Lists the buckets in the account and gives you handy commands for inspecting them further. | | AWS | [cape](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#cape) | Enumerates cross-account privilege escalation paths. Requires `pmapper` to be run first | | AWS | [cloudformation](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#cloudformation) | Lists the cloudformation stacks in the account. Generates loot file with stack details, stack parameters, and stack output - look for secrets. | diff --git a/aws/bedrock-agents.go b/aws/bedrock-agents.go new file mode 100644 index 00000000..18f8a3d1 --- /dev/null +++ b/aws/bedrock-agents.go @@ -0,0 +1,507 @@ +package aws + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + + "github.com/BishopFox/cloudfox/aws/sdk" + "github.com/BishopFox/cloudfox/internal" + "github.com/aws/aws-sdk-go-v2/aws" + bedrockagentTypes "github.com/aws/aws-sdk-go-v2/service/bedrockagent/types" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/bishopfox/awsservicemap" + "github.com/sirupsen/logrus" +) + +type BedrockAgentsModule struct { + BedrockAgentClient sdk.BedrockAgentClientInterface + + Caller sts.GetCallerIdentityOutput + AWSRegions []string + AWSProfile string + Goroutines int + WrapTable bool + AWSOutputType string + AWSTableCols string + ServiceMap *awsservicemap.AwsServiceMap + + Agents []BedrockAgent + CommandCounter internal.CommandCounter + output internal.OutputData2 + modLog *logrus.Entry +} + +type BedrockAgent struct { + Region string + Name string + AgentId string + AgentArn string + Status string + FoundationModel string + RoleArn string + Instruction string + ActionGroups int + Collaborators int + Aliases int + MemoryEnabled string + GuardrailId string + IdleSessionTTL int32 + LatestVersion string + ActionGroupDetails []BedrockActionGroupDetail +} + +type BedrockActionGroupDetail struct { + Name string + Id string + Description string + ExecutorType string + ExecutorArn string + ApiSchema string + Functions []BedrockActionGroupFunction +} + +type BedrockActionGroupFunction struct { + Name string + Description string + Parameters map[string]string +} + +func (m *BedrockAgentsModule) PrintBedrockAgents(outputDirectory string, verbosity int) { + m.output.Verbosity = verbosity + m.output.Directory = outputDirectory + m.output.CallingModule = "bedrock-agents" + m.modLog = internal.TxtLog.WithFields(logrus.Fields{ + "module": m.output.CallingModule, + }) + if m.AWSProfile == "" { + m.AWSProfile = internal.BuildAWSPath(m.Caller) + } + + fmt.Printf("[%s][%s] Enumerating Bedrock agents for account %s.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), aws.ToString(m.Caller.Account)) + + wg := new(sync.WaitGroup) + semaphore := make(chan struct{}, m.Goroutines) + + spinnerDone := make(chan bool) + go internal.SpinUntil(m.output.CallingModule, &m.CommandCounter, spinnerDone, "regions") + + dataReceiver := make(chan BedrockAgent) + receiverDone := make(chan bool) + go m.Receiver(dataReceiver, receiverDone) + + for _, region := range m.AWSRegions { + wg.Add(1) + m.CommandCounter.IncrPending() + go m.executeChecks(region, wg, semaphore, dataReceiver) + } + wg.Wait() + + spinnerDone <- true + <-spinnerDone + receiverDone <- true + <-receiverDone + + m.output.Headers = []string{ + "Account", + "Region", + "Name", + "AgentId", + "Status", + "FoundationModel", + "RoleArn", + "ActionGroups", + "Collaborators", + "AgentArn", + "IdleSessionTTL", + "Aliases", + "MemoryEnabled", + "GuardrailId", + } + + var tableCols []string + if m.AWSTableCols != "" { + m.AWSTableCols = strings.ReplaceAll(m.AWSTableCols, ", ", ",") + m.AWSTableCols = strings.ReplaceAll(m.AWSTableCols, ", ", ",") + tableCols = strings.Split(m.AWSTableCols, ",") + } else if m.AWSOutputType == "wide" { + tableCols = []string{ + "Account", + "Region", + "Name", + "Status", + "FoundationModel", + "RoleArn", + "ActionGroups", + "Collaborators", + "AgentArn", + "IdleSessionTTL", + "Aliases", + "MemoryEnabled", + "GuardrailId", + } + } else { + tableCols = []string{ + "Account", + "Region", + "Name", + "Status", + "FoundationModel", + "RoleArn", + "ActionGroups", + "Collaborators", + } + } + + for _, agent := range m.Agents { + m.output.Body = append( + m.output.Body, + []string{ + aws.ToString(m.Caller.Account), + agent.Region, + agent.Name, + agent.AgentId, + agent.Status, + agent.FoundationModel, + agent.RoleArn, + strconv.Itoa(agent.ActionGroups), + strconv.Itoa(agent.Collaborators), + agent.AgentArn, + strconv.Itoa(int(agent.IdleSessionTTL)), + strconv.Itoa(agent.Aliases), + agent.MemoryEnabled, + agent.GuardrailId, + }, + ) + } + + if len(m.output.Body) > 0 { + m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", fmt.Sprintf("%s-%s", m.AWSProfile, aws.ToString(m.Caller.Account))) + o := internal.OutputClient{ + Verbosity: verbosity, + CallingModule: m.output.CallingModule, + Table: internal.TableClient{ + Wrap: m.WrapTable, + }, + } + o.Table.TableFiles = append(o.Table.TableFiles, internal.TableFile{ + Header: m.output.Headers, + Body: m.output.Body, + TableCols: tableCols, + Name: m.output.CallingModule, + }) + o.PrefixIdentifier = m.AWSProfile + o.Table.DirectoryName = filepath.Join(outputDirectory, "cloudfox-output", "aws", fmt.Sprintf("%s-%s", m.AWSProfile, aws.ToString(m.Caller.Account))) + o.WriteFullOutput(o.Table.TableFiles, nil) + m.writeLoot(o.Table.DirectoryName, verbosity) + fmt.Printf("[%s][%s] %s Bedrock agents found.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), strconv.Itoa(len(m.output.Body))) + } else { + fmt.Printf("[%s][%s] No Bedrock agents found, skipping the creation of an output file.\n", cyan(m.output.CallingModule), cyan(m.AWSProfile)) + } + fmt.Printf("[%s][%s] For context and next steps: https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#%s\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), m.output.CallingModule) +} + +func (m *BedrockAgentsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan BedrockAgent) { + defer wg.Done() + + servicemap := m.ServiceMap + if servicemap == nil { + servicemap = &awsservicemap.AwsServiceMap{ + JsonFileSource: "DOWNLOAD_FROM_AWS", + } + } + res, err := servicemap.IsServiceInRegion("bedrock", r) + if err != nil { + m.modLog.Error(err) + } + if res { + m.CommandCounter.IncrTotal() + wg.Add(1) + m.getBedrockAgentsPerRegion(r, wg, semaphore, dataReceiver) + } +} + +func (m *BedrockAgentsModule) Receiver(receiver chan BedrockAgent, receiverDone chan bool) { + defer close(receiverDone) + for { + select { + case data := <-receiver: + m.Agents = append(m.Agents, data) + case <-receiverDone: + receiverDone <- true + return + } + } +} + +func (m *BedrockAgentsModule) getBedrockAgentsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan BedrockAgent) { + defer func() { + m.CommandCounter.DecrExecuting() + m.CommandCounter.IncrComplete() + wg.Done() + }() + semaphore <- struct{}{} + defer func() { + <-semaphore + }() + + agents, err := sdk.CachedBedrockAgentListAgents(m.BedrockAgentClient, aws.ToString(m.Caller.Account), r) + if err != nil { + m.modLog.Error(err.Error()) + m.CommandCounter.IncrError() + return + } + + for _, agentSummary := range agents { + agentID := aws.ToString(agentSummary.AgentId) + agentVersion := aws.ToString(agentSummary.LatestAgentVersion) + if agentVersion == "" { + agentVersion = "DRAFT" + } + + agent, err := sdk.CachedBedrockAgentGetAgent(m.BedrockAgentClient, aws.ToString(m.Caller.Account), r, agentID) + if err != nil { + m.modLog.Error(err.Error()) + m.CommandCounter.IncrError() + continue + } + + aliases, _ := sdk.CachedBedrockAgentListAgentAliases(m.BedrockAgentClient, aws.ToString(m.Caller.Account), r, agentID) + actionGroupSummaries, _ := sdk.CachedBedrockAgentListAgentActionGroups(m.BedrockAgentClient, aws.ToString(m.Caller.Account), r, agentID, agentVersion) + collaborators, _ := sdk.CachedBedrockAgentListAgentCollaborators(m.BedrockAgentClient, aws.ToString(m.Caller.Account), r, agentID, agentVersion) + + var actionGroupDetails []BedrockActionGroupDetail + for _, agSummary := range actionGroupSummaries { + agDetail := m.getActionGroupDetail(r, agentID, agentVersion, agSummary) + actionGroupDetails = append(actionGroupDetails, agDetail) + } + + memoryEnabled := "No" + if agent.MemoryConfiguration != nil && len(agent.MemoryConfiguration.EnabledMemoryTypes) > 0 { + memoryEnabled = "Yes" + } + + guardrailId := "" + if agent.GuardrailConfiguration != nil { + guardrailId = aws.ToString(agent.GuardrailConfiguration.GuardrailIdentifier) + } + + var idleSessionTTL int32 + if agent.IdleSessionTTLInSeconds != nil { + idleSessionTTL = *agent.IdleSessionTTLInSeconds + } + + dataReceiver <- BedrockAgent{ + Region: r, + Name: aws.ToString(agent.AgentName), + AgentId: agentID, + AgentArn: aws.ToString(agent.AgentArn), + Status: string(agent.AgentStatus), + FoundationModel: aws.ToString(agent.FoundationModel), + RoleArn: aws.ToString(agent.AgentResourceRoleArn), + Instruction: aws.ToString(agent.Instruction), + ActionGroups: len(actionGroupSummaries), + Collaborators: len(collaborators), + Aliases: len(aliases), + MemoryEnabled: memoryEnabled, + GuardrailId: guardrailId, + IdleSessionTTL: idleSessionTTL, + LatestVersion: agentVersion, + ActionGroupDetails: actionGroupDetails, + } + } +} + +func (m *BedrockAgentsModule) getActionGroupDetail(region string, agentID string, agentVersion string, summary bedrockagentTypes.ActionGroupSummary) BedrockActionGroupDetail { + agID := aws.ToString(summary.ActionGroupId) + detail := BedrockActionGroupDetail{ + Name: aws.ToString(summary.ActionGroupName), + Id: agID, + } + + ag, err := sdk.CachedBedrockAgentGetAgentActionGroup(m.BedrockAgentClient, aws.ToString(m.Caller.Account), region, agentID, agentVersion, agID) + if err != nil { + m.modLog.Error(err.Error()) + return detail + } + detail.Description = aws.ToString(ag.Description) + + switch v := ag.ActionGroupExecutor.(type) { + case *bedrockagentTypes.ActionGroupExecutorMemberLambda: + detail.ExecutorType = "Lambda" + detail.ExecutorArn = v.Value + case *bedrockagentTypes.ActionGroupExecutorMemberCustomControl: + detail.ExecutorType = "ReturnControl" + } + + switch v := ag.ApiSchema.(type) { + case *bedrockagentTypes.APISchemaMemberPayload: + detail.ApiSchema = v.Value + case *bedrockagentTypes.APISchemaMemberS3: + detail.ApiSchema = fmt.Sprintf("s3://%s/%s", aws.ToString(v.Value.S3BucketName), aws.ToString(v.Value.S3ObjectKey)) + } + + switch v := ag.FunctionSchema.(type) { + case *bedrockagentTypes.FunctionSchemaMemberFunctions: + for _, fn := range v.Value { + f := BedrockActionGroupFunction{ + Name: aws.ToString(fn.Name), + Description: aws.ToString(fn.Description), + Parameters: make(map[string]string), + } + for paramName, paramDetail := range fn.Parameters { + required := "" + if paramDetail.Required != nil && *paramDetail.Required { + required = ",required" + } + f.Parameters[paramName] = fmt.Sprintf("%s%s", paramDetail.Type, required) + } + detail.Functions = append(detail.Functions, f) + } + } + + return detail +} + +func (m *BedrockAgentsModule) writeLoot(outputDirectory string, verbosity int) { + path := filepath.Join(outputDirectory, "loot") + err := os.MkdirAll(path, os.ModePerm) + if err != nil { + m.modLog.Error(err.Error()) + m.CommandCounter.IncrError() + } + + m.writeLootCommands(path, verbosity) + m.writeLootDetails(path, verbosity) +} + +func (m *BedrockAgentsModule) writeLootCommands(lootDir string, verbosity int) { + commandsFile := filepath.Join(lootDir, "bedrock-agents-commands.txt") + + var out string + out += fmt.Sprintln("#############################################") + out += fmt.Sprintln("# The profile you will use to perform these commands is most likely not the profile you used to run CloudFox") + out += fmt.Sprintln("# Set the $profile environment variable to the profile you are going to use.") + out += fmt.Sprintln("# E.g., export profile=dev-prod.") + out += fmt.Sprintln("#############################################") + out += fmt.Sprintln("") + + for _, agent := range m.Agents { + out += fmt.Sprintln("=============================================") + out += fmt.Sprintf("# Agent: %s (%s) in %s\n\n", agent.Name, agent.AgentId, agent.Region) + + out += "# Get full agent configuration including instruction/system prompt\n" + out += fmt.Sprintf("aws --profile $profile --region %s bedrock-agent get-agent --agent-id %s\n\n", agent.Region, agent.AgentId) + + out += "# List agent aliases (production routing)\n" + out += fmt.Sprintf("aws --profile $profile --region %s bedrock-agent list-agent-aliases --agent-id %s\n\n", agent.Region, agent.AgentId) + + out += "# List action groups (tool integrations)\n" + out += fmt.Sprintf("aws --profile $profile --region %s bedrock-agent list-agent-action-groups --agent-id %s --agent-version %s\n\n", agent.Region, agent.AgentId, agent.LatestVersion) + + for _, ag := range agent.ActionGroupDetails { + out += fmt.Sprintf("# Get action group detail: %s\n", ag.Name) + out += fmt.Sprintf("aws --profile $profile --region %s bedrock-agent get-agent-action-group --agent-id %s --agent-version %s --action-group-id %s\n\n", agent.Region, agent.AgentId, agent.LatestVersion, ag.Id) + } + + if agent.Collaborators > 0 { + out += "# List collaborators (supervisor/worker topology)\n" + out += fmt.Sprintf("aws --profile $profile --region %s bedrock-agent list-agent-collaborators --agent-id %s --agent-version %s\n\n", agent.Region, agent.AgentId, agent.LatestVersion) + } + } + + err := os.WriteFile(commandsFile, []byte(out), 0644) + if err != nil { + m.modLog.Error(err.Error()) + m.CommandCounter.IncrError() + } + + if verbosity > 2 { + fmt.Println() + fmt.Printf("[%s][%s] %s \n\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), green("Beginning of commands loot file.")) + fmt.Print(out) + fmt.Printf("[%s][%s] %s \n\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), green("End of commands loot file.")) + } + fmt.Printf("[%s][%s] Loot written to [%s]\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), commandsFile) +} + +func (m *BedrockAgentsModule) writeLootDetails(lootDir string, verbosity int) { + detailsFile := filepath.Join(lootDir, "bedrock-agents-details.txt") + + var out string + for _, agent := range m.Agents { + out += fmt.Sprintln("=============================================") + out += fmt.Sprintf("# Agent: %s\n", agent.Name) + out += fmt.Sprintf("# ID: %s | Region: %s\n", agent.AgentId, agent.Region) + out += fmt.Sprintf("# ARN: %s\n", agent.AgentArn) + out += fmt.Sprintf("# Status: %s\n", agent.Status) + out += fmt.Sprintf("# Foundation Model: %s\n", agent.FoundationModel) + out += fmt.Sprintf("# Execution Role: %s\n", agent.RoleArn) + out += fmt.Sprintf("# Memory Enabled: %s\n", agent.MemoryEnabled) + out += fmt.Sprintf("# Guardrail: %s\n", agent.GuardrailId) + out += fmt.Sprintf("# Idle Session TTL: %ds\n", agent.IdleSessionTTL) + out += fmt.Sprintf("# Action Groups: %d | Collaborators: %d | Aliases: %d\n", agent.ActionGroups, agent.Collaborators, agent.Aliases) + out += fmt.Sprintln("") + out += fmt.Sprintln("## Instruction / System Prompt:") + if agent.Instruction != "" { + out += fmt.Sprintln(agent.Instruction) + } else { + out += fmt.Sprintln("(none)") + } + out += fmt.Sprintln("") + + if len(agent.ActionGroupDetails) > 0 { + out += fmt.Sprintln("## Action Groups:") + for _, ag := range agent.ActionGroupDetails { + out += fmt.Sprintf(" [%s] %s\n", ag.Id, ag.Name) + if ag.Description != "" { + out += fmt.Sprintf(" Description: %s\n", ag.Description) + } + if ag.ExecutorType != "" { + out += fmt.Sprintf(" Executor: %s", ag.ExecutorType) + if ag.ExecutorArn != "" { + out += fmt.Sprintf(" -> %s", ag.ExecutorArn) + } + out += "\n" + } + if ag.ApiSchema != "" { + out += fmt.Sprintf(" API Schema:\n%s\n", indentBlock(ag.ApiSchema, " ")) + } + for _, fn := range ag.Functions { + out += fmt.Sprintf(" Function: %s\n", fn.Name) + if fn.Description != "" { + out += fmt.Sprintf(" Description: %s\n", fn.Description) + } + if len(fn.Parameters) > 0 { + params, _ := json.Marshal(fn.Parameters) + out += fmt.Sprintf(" Parameters: %s\n", string(params)) + } + } + out += fmt.Sprintln("") + } + } + } + + err := os.WriteFile(detailsFile, []byte(out), 0644) + if err != nil { + m.modLog.Error(err.Error()) + m.CommandCounter.IncrError() + } + + fmt.Printf("[%s][%s] Loot written to [%s]\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), detailsFile) +} + +func indentBlock(text string, prefix string) string { + lines := strings.Split(text, "\n") + for i, line := range lines { + if line != "" { + lines[i] = prefix + line + } + } + return strings.Join(lines, "\n") +} + diff --git a/aws/bedrock-agents_test.go b/aws/bedrock-agents_test.go new file mode 100644 index 00000000..8a04b064 --- /dev/null +++ b/aws/bedrock-agents_test.go @@ -0,0 +1,69 @@ +package aws + +import ( + "log" + "testing" + + "github.com/BishopFox/cloudfox/aws/sdk" + "github.com/BishopFox/cloudfox/internal" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/sts" +) + +func TestBedrockAgents(t *testing.T) { + subtests := []struct { + name string + outputDirectory string + verbosity int + testModule BedrockAgentsModule + expectedResult []BedrockAgent + }{ + { + name: "test_bedrock_agents", + outputDirectory: ".", + verbosity: 2, + testModule: BedrockAgentsModule{ + BedrockAgentClient: &sdk.MockedBedrockAgentClient{}, + Caller: sts.GetCallerIdentityOutput{Arn: aws.String("arn:aws:iam::123456789012:user/test"), Account: aws.String("123456789012")}, + AWSProfile: "test", + Goroutines: 3, + AWSRegions: AWSRegions, + }, + expectedResult: []BedrockAgent{ + { + Name: "LabAgent01", + AgentId: "AGENT001", + Status: "PREPARED", + FoundationModel: "us.anthropic.claude-sonnet-4-6", + ActionGroups: 1, + Collaborators: 1, + }, + { + Name: "ResearchWorker01", + AgentId: "AGENT002", + Status: "PREPARED", + FoundationModel: "us.anthropic.claude-sonnet-4-6", + ActionGroups: 1, + Collaborators: 0, + }, + }, + }, + } + internal.MockFileSystem(true) + for _, subtest := range subtests { + t.Run(subtest.name, func(t *testing.T) { + subtest.testModule.PrintBedrockAgents(subtest.outputDirectory, subtest.verbosity) + for index, expectedAgent := range subtest.expectedResult { + if index >= len(subtest.testModule.Agents) { + log.Fatalf("Expected %d agents but only found %d", len(subtest.expectedResult), len(subtest.testModule.Agents)) + } + if expectedAgent.Name != subtest.testModule.Agents[index].Name { + log.Fatalf("Agent name %s does not match expected name %s", subtest.testModule.Agents[index].Name, expectedAgent.Name) + } + if expectedAgent.ActionGroups != subtest.testModule.Agents[index].ActionGroups { + log.Fatalf("Agent %s action groups count %d does not match expected %d", expectedAgent.Name, subtest.testModule.Agents[index].ActionGroups, expectedAgent.ActionGroups) + } + } + }) + } +} diff --git a/aws/sdk/bedrockagent.go b/aws/sdk/bedrockagent.go new file mode 100644 index 00000000..d5b52b31 --- /dev/null +++ b/aws/sdk/bedrockagent.go @@ -0,0 +1,318 @@ +package sdk + +import ( + "context" + "encoding/gob" + "fmt" + + "github.com/BishopFox/cloudfox/internal" + "github.com/aws/aws-sdk-go-v2/service/bedrockagent" + bedrockagentTypes "github.com/aws/aws-sdk-go-v2/service/bedrockagent/types" + "github.com/patrickmn/go-cache" + "github.com/sirupsen/logrus" +) + +type BedrockAgentClientInterface interface { + ListAgents(context.Context, *bedrockagent.ListAgentsInput, ...func(*bedrockagent.Options)) (*bedrockagent.ListAgentsOutput, error) + GetAgent(context.Context, *bedrockagent.GetAgentInput, ...func(*bedrockagent.Options)) (*bedrockagent.GetAgentOutput, error) + ListAgentAliases(context.Context, *bedrockagent.ListAgentAliasesInput, ...func(*bedrockagent.Options)) (*bedrockagent.ListAgentAliasesOutput, error) + ListAgentActionGroups(context.Context, *bedrockagent.ListAgentActionGroupsInput, ...func(*bedrockagent.Options)) (*bedrockagent.ListAgentActionGroupsOutput, error) + GetAgentActionGroup(context.Context, *bedrockagent.GetAgentActionGroupInput, ...func(*bedrockagent.Options)) (*bedrockagent.GetAgentActionGroupOutput, error) + ListAgentCollaborators(context.Context, *bedrockagent.ListAgentCollaboratorsInput, ...func(*bedrockagent.Options)) (*bedrockagent.ListAgentCollaboratorsOutput, error) + GetAgentCollaborator(context.Context, *bedrockagent.GetAgentCollaboratorInput, ...func(*bedrockagent.Options)) (*bedrockagent.GetAgentCollaboratorOutput, error) +} + +func init() { + gob.Register([]bedrockagentTypes.AgentSummary{}) + gob.Register([]bedrockagentTypes.AgentAliasSummary{}) + gob.Register([]bedrockagentTypes.ActionGroupSummary{}) + gob.Register([]bedrockagentTypes.AgentCollaboratorSummary{}) + gob.Register(bedrockagentTypes.Agent{}) + gob.Register(&bedrockagentTypes.AgentActionGroup{}) + gob.Register(&bedrockagentTypes.ActionGroupExecutorMemberLambda{}) + gob.Register(&bedrockagentTypes.ActionGroupExecutorMemberCustomControl{}) + gob.Register(&bedrockagentTypes.APISchemaMemberPayload{}) + gob.Register(&bedrockagentTypes.APISchemaMemberS3{}) + gob.Register(&bedrockagentTypes.FunctionSchemaMemberFunctions{}) +} + +func CachedBedrockAgentListAgents(client BedrockAgentClientInterface, accountID string, region string) ([]bedrockagentTypes.AgentSummary, error) { + var PaginationControl *string + var agents []bedrockagentTypes.AgentSummary + cacheKey := fmt.Sprintf("%s-bedrockagent-ListAgents-%s", accountID, region) + cached, found := internal.Cache.Get(cacheKey) + if found { + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:ListAgents", + "account": accountID, + "region": region, + "cache": "hit", + }).Info("AWS API call") + return cached.([]bedrockagentTypes.AgentSummary), nil + } + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:ListAgents", + "account": accountID, + "region": region, + "cache": "miss", + }).Info("AWS API call") + + for { + ListAgents, err := client.ListAgents( + context.TODO(), + &bedrockagent.ListAgentsInput{ + NextToken: PaginationControl, + }, + func(o *bedrockagent.Options) { + o.Region = region + }, + ) + if err != nil { + return agents, err + } + + agents = append(agents, ListAgents.AgentSummaries...) + + if ListAgents.NextToken == nil { + break + } + PaginationControl = ListAgents.NextToken + } + internal.Cache.Set(cacheKey, agents, cache.DefaultExpiration) + + return agents, nil +} + +func CachedBedrockAgentListAgentAliases(client BedrockAgentClientInterface, accountID string, region string, agentID string) ([]bedrockagentTypes.AgentAliasSummary, error) { + var PaginationControl *string + var aliases []bedrockagentTypes.AgentAliasSummary + cacheKey := fmt.Sprintf("%s-bedrockagent-ListAgentAliases-%s-%s", accountID, region, agentID) + cached, found := internal.Cache.Get(cacheKey) + if found { + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:ListAgentAliases", + "account": accountID, + "region": region, + "agent": agentID, + "cache": "hit", + }).Info("AWS API call") + return cached.([]bedrockagentTypes.AgentAliasSummary), nil + } + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:ListAgentAliases", + "account": accountID, + "region": region, + "agent": agentID, + "cache": "miss", + }).Info("AWS API call") + + for { + ListAliases, err := client.ListAgentAliases( + context.TODO(), + &bedrockagent.ListAgentAliasesInput{ + AgentId: &agentID, + NextToken: PaginationControl, + }, + func(o *bedrockagent.Options) { + o.Region = region + }, + ) + if err != nil { + return aliases, err + } + + aliases = append(aliases, ListAliases.AgentAliasSummaries...) + + if ListAliases.NextToken == nil { + break + } + PaginationControl = ListAliases.NextToken + } + internal.Cache.Set(cacheKey, aliases, cache.DefaultExpiration) + + return aliases, nil +} + +func CachedBedrockAgentListAgentActionGroups(client BedrockAgentClientInterface, accountID string, region string, agentID string, agentVersion string) ([]bedrockagentTypes.ActionGroupSummary, error) { + var PaginationControl *string + var actionGroups []bedrockagentTypes.ActionGroupSummary + cacheKey := fmt.Sprintf("%s-bedrockagent-ListAgentActionGroups-%s-%s", accountID, region, agentID) + cached, found := internal.Cache.Get(cacheKey) + if found { + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:ListAgentActionGroups", + "account": accountID, + "region": region, + "agent": agentID, + "cache": "hit", + }).Info("AWS API call") + return cached.([]bedrockagentTypes.ActionGroupSummary), nil + } + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:ListAgentActionGroups", + "account": accountID, + "region": region, + "agent": agentID, + "cache": "miss", + }).Info("AWS API call") + + for { + ListActionGroups, err := client.ListAgentActionGroups( + context.TODO(), + &bedrockagent.ListAgentActionGroupsInput{ + AgentId: &agentID, + AgentVersion: &agentVersion, + NextToken: PaginationControl, + }, + func(o *bedrockagent.Options) { + o.Region = region + }, + ) + if err != nil { + return actionGroups, err + } + + actionGroups = append(actionGroups, ListActionGroups.ActionGroupSummaries...) + + if ListActionGroups.NextToken == nil { + break + } + PaginationControl = ListActionGroups.NextToken + } + internal.Cache.Set(cacheKey, actionGroups, cache.DefaultExpiration) + + return actionGroups, nil +} + +func CachedBedrockAgentListAgentCollaborators(client BedrockAgentClientInterface, accountID string, region string, agentID string, agentVersion string) ([]bedrockagentTypes.AgentCollaboratorSummary, error) { + var PaginationControl *string + var collaborators []bedrockagentTypes.AgentCollaboratorSummary + cacheKey := fmt.Sprintf("%s-bedrockagent-ListAgentCollaborators-%s-%s", accountID, region, agentID) + cached, found := internal.Cache.Get(cacheKey) + if found { + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:ListAgentCollaborators", + "account": accountID, + "region": region, + "agent": agentID, + "cache": "hit", + }).Info("AWS API call") + return cached.([]bedrockagentTypes.AgentCollaboratorSummary), nil + } + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:ListAgentCollaborators", + "account": accountID, + "region": region, + "agent": agentID, + "cache": "miss", + }).Info("AWS API call") + + for { + ListCollaborators, err := client.ListAgentCollaborators( + context.TODO(), + &bedrockagent.ListAgentCollaboratorsInput{ + AgentId: &agentID, + AgentVersion: &agentVersion, + NextToken: PaginationControl, + }, + func(o *bedrockagent.Options) { + o.Region = region + }, + ) + if err != nil { + return collaborators, err + } + + collaborators = append(collaborators, ListCollaborators.AgentCollaboratorSummaries...) + + if ListCollaborators.NextToken == nil { + break + } + PaginationControl = ListCollaborators.NextToken + } + internal.Cache.Set(cacheKey, collaborators, cache.DefaultExpiration) + + return collaborators, nil +} + +func CachedBedrockAgentGetAgent(client BedrockAgentClientInterface, accountID string, region string, agentID string) (bedrockagentTypes.Agent, error) { + var agent bedrockagentTypes.Agent + cacheKey := fmt.Sprintf("%s-bedrockagent-GetAgent-%s-%s", accountID, region, agentID) + cached, found := internal.Cache.Get(cacheKey) + if found { + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:GetAgent", + "account": accountID, + "region": region, + "agent": agentID, + "cache": "hit", + }).Info("AWS API call") + return cached.(bedrockagentTypes.Agent), nil + } + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:GetAgent", + "account": accountID, + "region": region, + "agent": agentID, + "cache": "miss", + }).Info("AWS API call") + + resp, err := client.GetAgent( + context.TODO(), + &bedrockagent.GetAgentInput{ + AgentId: &agentID, + }, + func(o *bedrockagent.Options) { + o.Region = region + }, + ) + if err != nil { + return agent, err + } + agent = *resp.Agent + internal.Cache.Set(cacheKey, agent, cache.DefaultExpiration) + + return agent, nil +} + +func CachedBedrockAgentGetAgentActionGroup(client BedrockAgentClientInterface, accountID string, region string, agentID string, agentVersion string, actionGroupID string) (*bedrockagentTypes.AgentActionGroup, error) { + cacheKey := fmt.Sprintf("%s-bedrockagent-GetAgentActionGroup-%s-%s-%s", accountID, region, agentID, actionGroupID) + cached, found := internal.Cache.Get(cacheKey) + if found { + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:GetAgentActionGroup", + "account": accountID, + "region": region, + "agent": agentID, + "actionGroup": actionGroupID, + "cache": "hit", + }).Info("AWS API call") + return cached.(*bedrockagentTypes.AgentActionGroup), nil + } + sharedLogger.WithFields(logrus.Fields{ + "api": "bedrockagent:GetAgentActionGroup", + "account": accountID, + "region": region, + "agent": agentID, + "actionGroup": actionGroupID, + "cache": "miss", + }).Info("AWS API call") + + resp, err := client.GetAgentActionGroup( + context.TODO(), + &bedrockagent.GetAgentActionGroupInput{ + AgentId: &agentID, + AgentVersion: &agentVersion, + ActionGroupId: &actionGroupID, + }, + func(o *bedrockagent.Options) { + o.Region = region + }, + ) + if err != nil { + return nil, err + } + internal.Cache.Set(cacheKey, resp.AgentActionGroup, cache.DefaultExpiration) + + return resp.AgentActionGroup, nil +} diff --git a/aws/sdk/bedrockagent_mocks.go b/aws/sdk/bedrockagent_mocks.go new file mode 100644 index 00000000..7d3d7213 --- /dev/null +++ b/aws/sdk/bedrockagent_mocks.go @@ -0,0 +1,151 @@ +package sdk + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/bedrockagent" + bedrockagentTypes "github.com/aws/aws-sdk-go-v2/service/bedrockagent/types" +) + +type MockedBedrockAgentClient struct{} + +func (m *MockedBedrockAgentClient) ListAgents(ctx context.Context, input *bedrockagent.ListAgentsInput, options ...func(*bedrockagent.Options)) (*bedrockagent.ListAgentsOutput, error) { + return &bedrockagent.ListAgentsOutput{ + AgentSummaries: []bedrockagentTypes.AgentSummary{ + { + AgentId: aws.String("AGENT001"), + AgentName: aws.String("LabAgent01"), + AgentStatus: bedrockagentTypes.AgentStatusPrepared, + LatestAgentVersion: aws.String("1"), + }, + { + AgentId: aws.String("AGENT002"), + AgentName: aws.String("ResearchWorker01"), + AgentStatus: bedrockagentTypes.AgentStatusPrepared, + LatestAgentVersion: aws.String("1"), + }, + }, + }, nil +} + +func (m *MockedBedrockAgentClient) GetAgent(ctx context.Context, input *bedrockagent.GetAgentInput, options ...func(*bedrockagent.Options)) (*bedrockagent.GetAgentOutput, error) { + agents := map[string]*bedrockagent.GetAgentOutput{ + "AGENT001": { + Agent: &bedrockagentTypes.Agent{ + AgentId: aws.String("AGENT001"), + AgentName: aws.String("LabAgent01"), + AgentArn: aws.String("arn:aws:bedrock:us-east-1:123456789012:agent/AGENT001"), + AgentStatus: bedrockagentTypes.AgentStatusPrepared, + FoundationModel: aws.String("us.anthropic.claude-sonnet-4-6"), + AgentResourceRoleArn: aws.String("arn:aws:iam::123456789012:role/AgentExecutionRole-Lab"), + Instruction: aws.String("You are a helpful order management assistant."), + IdleSessionTTLInSeconds: aws.Int32(600), + }, + }, + "AGENT002": { + Agent: &bedrockagentTypes.Agent{ + AgentId: aws.String("AGENT002"), + AgentName: aws.String("ResearchWorker01"), + AgentArn: aws.String("arn:aws:bedrock:us-east-1:123456789012:agent/AGENT002"), + AgentStatus: bedrockagentTypes.AgentStatusPrepared, + FoundationModel: aws.String("us.anthropic.claude-sonnet-4-6"), + AgentResourceRoleArn: aws.String("arn:aws:iam::123456789012:role/WorkerExecutionRole-Lab"), + Instruction: aws.String("You are a read-only research agent."), + IdleSessionTTLInSeconds: aws.Int32(300), + }, + }, + } + if out, ok := agents[aws.ToString(input.AgentId)]; ok { + return out, nil + } + return agents["AGENT001"], nil +} + +func (m *MockedBedrockAgentClient) ListAgentAliases(ctx context.Context, input *bedrockagent.ListAgentAliasesInput, options ...func(*bedrockagent.Options)) (*bedrockagent.ListAgentAliasesOutput, error) { + return &bedrockagent.ListAgentAliasesOutput{ + AgentAliasSummaries: []bedrockagentTypes.AgentAliasSummary{ + { + AgentAliasId: aws.String("ALIAS001"), + AgentAliasName: aws.String("lab-v1"), + }, + }, + }, nil +} + +func (m *MockedBedrockAgentClient) ListAgentActionGroups(ctx context.Context, input *bedrockagent.ListAgentActionGroupsInput, options ...func(*bedrockagent.Options)) (*bedrockagent.ListAgentActionGroupsOutput, error) { + return &bedrockagent.ListAgentActionGroupsOutput{ + ActionGroupSummaries: []bedrockagentTypes.ActionGroupSummary{ + { + ActionGroupId: aws.String("AG001"), + ActionGroupName: aws.String("OrderLookupActionGroup"), + ActionGroupState: bedrockagentTypes.ActionGroupStateEnabled, + }, + }, + }, nil +} + +func (m *MockedBedrockAgentClient) GetAgentActionGroup(ctx context.Context, input *bedrockagent.GetAgentActionGroupInput, options ...func(*bedrockagent.Options)) (*bedrockagent.GetAgentActionGroupOutput, error) { + return &bedrockagent.GetAgentActionGroupOutput{ + AgentActionGroup: &bedrockagentTypes.AgentActionGroup{ + ActionGroupId: aws.String("AG001"), + ActionGroupName: aws.String("OrderLookupActionGroup"), + ActionGroupState: bedrockagentTypes.ActionGroupStateEnabled, + AgentId: input.AgentId, + AgentVersion: input.AgentVersion, + ActionGroupExecutor: &bedrockagentTypes.ActionGroupExecutorMemberLambda{ + Value: "arn:aws:lambda:us-east-1:123456789012:function:order_lookup_fn", + }, + Description: aws.String("Looks up order status by order ID"), + FunctionSchema: &bedrockagentTypes.FunctionSchemaMemberFunctions{ + Value: []bedrockagentTypes.Function{ + { + Name: aws.String("lookupOrder"), + Description: aws.String("Look up an order by its ID"), + Parameters: map[string]bedrockagentTypes.ParameterDetail{ + "orderId": { + Type: bedrockagentTypes.TypeString, + Description: aws.String("The order identifier"), + Required: aws.Bool(true), + }, + }, + }, + }, + }, + }, + }, nil +} + +func (m *MockedBedrockAgentClient) ListAgentCollaborators(ctx context.Context, input *bedrockagent.ListAgentCollaboratorsInput, options ...func(*bedrockagent.Options)) (*bedrockagent.ListAgentCollaboratorsOutput, error) { + if aws.ToString(input.AgentId) == "AGENT001" { + return &bedrockagent.ListAgentCollaboratorsOutput{ + AgentCollaboratorSummaries: []bedrockagentTypes.AgentCollaboratorSummary{ + { + AgentDescriptor: &bedrockagentTypes.AgentDescriptor{ + AliasArn: aws.String("arn:aws:bedrock:us-east-1:123456789012:agent-alias/AGENT002/ALIAS002"), + }, + CollaboratorId: aws.String("COLLAB001"), + CollaboratorName: aws.String("ResearchWorker01"), + RelayConversationHistory: bedrockagentTypes.RelayConversationHistoryDisabled, + }, + }, + }, nil + } + return &bedrockagent.ListAgentCollaboratorsOutput{ + AgentCollaboratorSummaries: []bedrockagentTypes.AgentCollaboratorSummary{}, + }, nil +} + +func (m *MockedBedrockAgentClient) GetAgentCollaborator(ctx context.Context, input *bedrockagent.GetAgentCollaboratorInput, options ...func(*bedrockagent.Options)) (*bedrockagent.GetAgentCollaboratorOutput, error) { + return &bedrockagent.GetAgentCollaboratorOutput{ + AgentCollaborator: &bedrockagentTypes.AgentCollaborator{ + AgentDescriptor: &bedrockagentTypes.AgentDescriptor{ + AliasArn: aws.String("arn:aws:bedrock:us-east-1:123456789012:agent-alias/AGENT002/ALIAS002"), + }, + CollaboratorId: aws.String("COLLAB001"), + CollaboratorName: aws.String("ResearchWorker01"), + CollaborationInstruction: aws.String("Route research and read-only queries to this worker"), + RelayConversationHistory: bedrockagentTypes.RelayConversationHistoryDisabled, + }, + }, nil +} diff --git a/cli/aws.go b/cli/aws.go index d1646708..57b9c133 100644 --- a/cli/aws.go +++ b/cli/aws.go @@ -13,6 +13,7 @@ import ( "github.com/BishopFox/cloudfox/aws/sdk" "github.com/BishopFox/cloudfox/internal" "github.com/BishopFox/cloudfox/internal/common" + "github.com/aws/aws-sdk-go-v2/service/bedrockagent" "github.com/aws/aws-sdk-go-v2/service/apigateway" "github.com/aws/aws-sdk-go-v2/service/apigatewayv2" "github.com/aws/aws-sdk-go-v2/service/apprunner" @@ -103,6 +104,17 @@ var ( }, } + BedrockAgentsCommand = &cobra.Command{ + Use: "bedrock-agents", + Aliases: []string{"bedrock-agent", "agents"}, + Short: "Enumerate Bedrock agents, action groups, collaborators, and aliases", + Long: "\nUse case examples:\n" + + os.Args[0] + " aws bedrock-agents --profile readonly_profile", + PreRun: awsPreRun, + Run: runBedrockAgentsCommand, + PostRun: awsPostRun, + } + AccessKeysFilter string AccessKeysCommand = &cobra.Command{ Use: "access-keys", @@ -1796,6 +1808,32 @@ func runSecretsCommand(cmd *cobra.Command, args []string) { } } +func runBedrockAgentsCommand(cmd *cobra.Command, args []string) { + for _, profile := range AWSProfiles { + sharedServiceMap := &awsservicemap.AwsServiceMap{ + JsonFileSource: "DOWNLOAD_FROM_AWS", + } + var AWSConfig = internal.AWSConfigFileLoader(profile, cmd.Root().Version, AWSMFAToken) + caller, err := internal.AWSWhoami(profile, cmd.Root().Version, AWSMFAToken) + if err != nil { + continue + } + m := aws.BedrockAgentsModule{ + BedrockAgentClient: bedrockagent.NewFromConfig(AWSConfig), + + Caller: *caller, + AWSRegions: internal.GetEnabledRegions(profile, cmd.Root().Version, AWSMFAToken), + AWSProfile: profile, + Goroutines: Goroutines, + WrapTable: AWSWrapTable, + AWSOutputType: AWSOutputType, + AWSTableCols: AWSTableCols, + ServiceMap: sharedServiceMap, + } + m.PrintBedrockAgents(AWSOutputDirectory, Verbosity) + } +} + func runTagsCommand(cmd *cobra.Command, args []string) { for _, profile := range AWSProfiles { var AWSConfig = internal.AWSConfigFileLoader(profile, cmd.Root().Version, AWSMFAToken) @@ -2558,6 +2596,7 @@ func init() { AWSCommands.AddCommand( AccessKeysCommand, AllChecksCommand, + BedrockAgentsCommand, ApiGwCommand, BucketsCommand, CapeCommand, diff --git a/go.mod b/go.mod index 6d3e9e7e..cae64a5f 100644 --- a/go.mod +++ b/go.mod @@ -16,13 +16,14 @@ require ( github.com/Azure/go-autorest/autorest v0.11.30 github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 github.com/aquasecurity/table v1.11.0 - github.com/aws/aws-sdk-go-v2 v1.41.5 + github.com/aws/aws-sdk-go-v2 v1.42.0 github.com/aws/aws-sdk-go-v2/config v1.32.5 github.com/aws/aws-sdk-go-v2/credentials v1.19.5 github.com/aws/aws-sdk-go-v2/service/apigateway v1.38.3 github.com/aws/aws-sdk-go-v2/service/apigatewayv2 v1.33.4 github.com/aws/aws-sdk-go-v2/service/apprunner v1.39.9 github.com/aws/aws-sdk-go-v2/service/athena v1.56.4 + github.com/aws/aws-sdk-go-v2/service/bedrockagent v1.54.6 github.com/aws/aws-sdk-go-v2/service/cloud9 v1.33.15 github.com/aws/aws-sdk-go-v2/service/cloudformation v1.71.4 github.com/aws/aws-sdk-go-v2/service/cloudfront v1.58.3 @@ -68,7 +69,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sqs v1.42.20 github.com/aws/aws-sdk-go-v2/service/ssm v1.67.7 github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 - github.com/aws/smithy-go v1.24.2 + github.com/aws/smithy-go v1.27.1 github.com/bishopfox/awsservicemap v1.1.0 github.com/bishopfox/knownawsaccountslookup v0.0.0-20231228165844-c37ef8df33cb github.com/dominikbraun/graph v0.23.0 @@ -170,8 +171,8 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect diff --git a/go.sum b/go.sum index cce23aea..d28dbd42 100644 --- a/go.sum +++ b/go.sum @@ -116,6 +116,8 @@ github.com/aquasecurity/table v1.11.0 h1:SzgCAv7dZcv/gyAyzxorS6OgEk7w/WU5iT2pStI github.com/aquasecurity/table v1.11.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw= github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA= +github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= github.com/aws/aws-sdk-go-v2/config v1.32.5 h1:pz3duhAfUgnxbtVhIK39PGF/AHYyrzGEyRD9Og0QrE8= @@ -126,8 +128,12 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBU github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ= @@ -140,6 +146,8 @@ github.com/aws/aws-sdk-go-v2/service/apprunner v1.39.9 h1:3MgcobMoBK3IqP2TbuySbd github.com/aws/aws-sdk-go-v2/service/apprunner v1.39.9/go.mod h1:n6b+O7QJ6E37dXZYPdLnC4S7Cc5HUYOQPZijLeDKIGY= github.com/aws/aws-sdk-go-v2/service/athena v1.56.4 h1:kCHAYlcGKCKKDGMRUfnY6pI992vC3UdA3+oPdfDBSHE= github.com/aws/aws-sdk-go-v2/service/athena v1.56.4/go.mod h1:Whob/cKMykIKxGMQVhPhGEC1+D+bgEDOL7brjSB4ju8= +github.com/aws/aws-sdk-go-v2/service/bedrockagent v1.54.6 h1:4Z9EIZUF8dkVDYTEt7NxKhA3yE3S/x7iWYL/C+reRgw= +github.com/aws/aws-sdk-go-v2/service/bedrockagent v1.54.6/go.mod h1:GlwXK7c/0CMTSU4a0PAAp5jiYXH55DRYT3mliKlqf6M= github.com/aws/aws-sdk-go-v2/service/cloud9 v1.33.15 h1:VKLLGKz1jfsQMf/tCjqiqzwha+5n8bcndAqH4/yMJEk= github.com/aws/aws-sdk-go-v2/service/cloud9 v1.33.15/go.mod h1:6eUG6qcUwAACFF/b0fIlZF0sSUwRwY2yJaJ0KoRNjF8= github.com/aws/aws-sdk-go-v2/service/cloudformation v1.71.4 h1:9dwMueqbHIp0KTw2Zt0rhVobiPMlAI8UgyxiaBzM+1E= @@ -250,6 +258,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8= +github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=