Compare commits

...
24 Commits
Author SHA1 Message Date
Seth Art 29bfd2c863 Merge pull request #49 from BishopFox/seth-dev
bumped version to fix another local filesystem write
2023-09-06 14:05:38 -04:00
sethsec-bf 40e0c92ef1 another fix to pass brew tests 2023-09-06 14:04:50 -04:00
sethsec-bf 9f8ae08262 another fix to pass brew tests 2023-09-06 14:03:52 -04:00
Seth Art c85aa5de89 Merge pull request #48 from BishopFox/seth-dev
v1.12.1
2023-09-06 11:31:45 -04:00
sethsec-bf 42d7cfef1e hacky fix to have error log written to local dir if user dir does not exist (so it can pass brew test) 2023-09-05 18:04:25 -04:00
Seth Art a5bce5ad8c Merge pull request #46 from BishopFox/seth-dev
v1.12.0
2023-08-15 14:21:50 -04:00
sethsec-bf 8221b5ff6d bumped version to 1.12.0 2023-08-15 14:20:18 -04:00
sethsec-bf aa42c8b808 renamed azure instances -> vms, got userdata loot working on azure vms command 2023-08-14 13:43:13 -04:00
sethsec-bf 5aab3b4bea updated logging path for tenant data to make it more clear 2023-08-10 15:50:54 -04:00
sethsec-bf 01d320d64d inventory now supports tenant mode, both merged and split. fixed all tests 2023-08-10 14:54:25 -04:00
sethsec-bf 5132622a13 updated azure to use same home directory logic as the aws command 2023-08-10 14:53:22 -04:00
sethsec-bf 08b5cd17af renamed rbac and storage, and converted instances and storage over to user names in addition to IDs as input params. 2023-08-09 10:27:44 -04:00
sethsec-bf 102b91e2c7 Got rbac working with the new directory stucture and accepting common names for tentant and subscirption as input params 2023-08-08 13:02:49 -04:00
sethsec-bf 3a19fdb2fe reworking the tenant/subscription file structure 2023-07-24 09:50:59 -04:00
sethsec-bf d94fafe2b4 go get -u 2023-07-18 15:46:43 -04:00
sethsec-bf 00ed8a7336 Added many new resources to inventory. Made inventory rows dynamic. Only non-empty rows will print 2023-07-18 15:12:25 -04:00
sethsec-bf 60daff386c updated inventory to include more redshift, route53, updated route53 to use new cached functions and to include CNAME records in loot 2023-07-12 16:19:53 -04:00
sethsec-bf cef1aa3a82 updated inventory to include more ec2, ecs, and ecr resources 2023-07-11 09:30:09 -04:00
sethsec-bf e88cb77394 updated inventory to include more ec2, ecs, and ecr resources 2023-07-11 09:29:03 -04:00
sethsec-bf fc52e01192 updated tests, added new sdk functions, added started the migration of keeping all mocks in _mocks.go files 2023-07-10 09:37:06 -04:00
sethsec-bf 659b2e7248 Completed inventory migration to use sdk, also finally removed need for second global table 2023-07-06 22:50:44 -04:00
sethsec-bf 06a7e7ec28 Updated sdk package to do the gob initializations in self contained init functions 2023-07-06 14:35:57 -04:00
sethsec-bf 602332a0c5 Updated ecs-tasks and eni to use cached functions 2023-07-06 12:19:45 -04:00
sethsec-bf 471da59da2 Updated ecs-tasks and eni to use cached functions 2023-07-06 11:29:21 -04:00
116 changed files with 7410 additions and 1695 deletions

No files matched your search

+2 -1
View File
@@ -69,4 +69,5 @@ cloudfox
*.txt
*.json
*.csv
*.log
*.log
dist/
+1 -1
View File
@@ -112,7 +112,7 @@ func TestListBuckets(t *testing.T) {
t.Run(subtest.name, func(t *testing.T) {
subtest.testModule.PrintBuckets(subtest.testModule.OutputFormat, subtest.outputDirectory, subtest.verbosity)
for index, expectedBucket := range subtest.expectedResult {
resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/123456789012-unittesting/table/buckets.txt")
resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/unittesting-123456789012/table/buckets.txt")
resultsFile, err := afero.ReadFile(fs, resultsFilePath)
if err != nil {
t.Fatalf("Cannot read output file at %s: %s", resultsFilePath, err)
+2 -68
View File
@@ -1,81 +1,15 @@
package aws
import (
"context"
"encoding/json"
"log"
"testing"
"time"
"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/cloudformation"
"github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
const DESCRIBE_STACKS_TEST_FILE = "./test-data/cloudformation-describestacks.json"
const TEMPLATE_BODY_TEST_FILE = "./test-data/cloudformation-getTemplate.json"
type Stacks struct {
Stacks []struct {
StackID string `json:"StackId"`
Description string `json:"Description"`
Tags []interface{} `json:"Tags"`
Outputs []types.Output `json:"Outputs"`
Parameters []types.Parameter `json:"Parameters"`
StackStatusReason interface{} `json:"StackStatusReason"`
CreationTime time.Time `json:"CreationTime"`
Capabilities []interface{} `json:"Capabilities"`
StackName string `json:"StackName"`
RoleArn string `json:"RoleArn"`
StackStatus string `json:"StackStatus"`
DisableRollback bool `json:"DisableRollback"`
} `json:"Stacks"`
}
type TemplateBody struct {
TemplateBody string `json:"TemplateBody"`
}
type MockedCloudformationClient struct {
describeStacks Stacks
getTemplateBody TemplateBody
}
func (m *MockedCloudformationClient) DescribeStacks(ctx context.Context, params *cloudformation.DescribeStacksInput, optFns ...func(*cloudformation.Options)) (*cloudformation.DescribeStacksOutput, error) {
err := json.Unmarshal(readTestFile(DESCRIBE_STACKS_TEST_FILE), &m.describeStacks)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_STACKS_TEST_FILE)
}
var stacks []types.Stack
for _, stack := range m.describeStacks.Stacks {
stacks = append(stacks, types.Stack{
StackName: &stack.StackName,
RoleARN: &stack.RoleArn,
Outputs: stack.Outputs,
Parameters: stack.Parameters,
})
}
return &cloudformation.DescribeStacksOutput{Stacks: stacks}, nil
}
func (m *MockedCloudformationClient) GetTemplate(ctx context.Context, params *cloudformation.GetTemplateInput, optFns ...func(*cloudformation.Options)) (*cloudformation.GetTemplateOutput, error) {
err := json.Unmarshal(readTestFile(DESCRIBE_STACKS_TEST_FILE), &m.getTemplateBody)
if err != nil {
log.Fatalf("can't unmarshall file %s", TEMPLATE_BODY_TEST_FILE)
}
return &cloudformation.GetTemplateOutput{TemplateBody: &m.getTemplateBody.TemplateBody}, nil
}
func (m *MockedCloudformationClient) ListStacks(ctx context.Context, params *cloudformation.ListStacksInput, optFns ...func(*cloudformation.Options)) (*cloudformation.ListStacksOutput, error) {
return &cloudformation.ListStacksOutput{}, nil
}
func TestCloudFormation(t *testing.T) {
subtests := []struct {
name string
@@ -89,7 +23,7 @@ func TestCloudFormation(t *testing.T) {
outputDirectory: ".",
verbosity: 2,
testModule: CloudformationModule{
CloudFormationClient: &MockedCloudformationClient{},
CloudFormationClient: &sdk.MockedCloudformationClient{},
Caller: sts.GetCallerIdentityOutput{Arn: aws.String("test")},
OutputFormat: "table",
AWSProfile: "test",
+1 -1
View File
@@ -126,7 +126,7 @@ func TestCodeBuildProjects(t *testing.T) {
// execute the module with verbosity set to 2
m.PrintCodeBuildProjects("table", tmpDir, 2)
resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/123456789012-unittesting/table/codebuild.txt")
resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/unittesting-123456789012/table/codebuild.txt")
resultsFile, err := afero.ReadFile(fs, resultsFilePath)
if err != nil {
t.Fatalf("Cannot read output file at %s: %s", resultsFilePath, err)
+1 -1
View File
@@ -19,7 +19,7 @@ import (
type DatabasesModule struct {
RDSClient sdk.RDSClientInterface
RedshiftClient sdk.RedShiftClientInterface
RedshiftClient sdk.AWSRedShiftClientInterface
DynamoDBClient sdk.DynamoDBClientInterface
DocDBClient sdk.DocDBClientInterface
+38 -98
View File
@@ -13,7 +13,6 @@ import (
"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/ec2"
"github.com/aws/aws-sdk-go-v2/service/ecs"
"github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
@@ -21,16 +20,10 @@ import (
"github.com/sirupsen/logrus"
)
type DescribeTasksDefinitionAPIClient interface {
DescribeTaskDefinition(context.Context, *ecs.DescribeTaskDefinitionInput, ...func(*ecs.Options)) (*ecs.DescribeTaskDefinitionOutput, error)
}
type ECSTasksModule struct {
DescribeTaskDefinitionClient DescribeTasksDefinitionAPIClient
DescribeTasksClient ecs.DescribeTasksAPIClient
ListTasksClient ecs.ListTasksAPIClient
ListClustersClient ecs.ListClustersAPIClient
DescribeNetworkInterfacesClient ec2.DescribeNetworkInterfacesAPIClient
IAMClient sdk.AWSIAMClientInterface
ECSClient sdk.AWSECSClientInterface
EC2Client sdk.AWSEC2ClientInterface
IAMClient sdk.AWSIAMClientInterface
Caller sts.GetCallerIdentityOutput
AWSRegions []string
@@ -313,75 +306,37 @@ func (m *ECSTasksModule) executeChecks(r string, wg *sync.WaitGroup, dataReceive
func (m *ECSTasksModule) getListClusters(region string, dataReceiver chan MappedECSTask) {
var PaginationControl *string
for {
ListClusters, err := m.ListClustersClient.ListClusters(
context.TODO(),
&(ecs.ListClustersInput{
NextToken: PaginationControl,
}),
func(o *ecs.Options) {
o.Region = region
},
)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
break
}
for _, clusterARN := range ListClusters.ClusterArns {
m.getListTasks(clusterARN, region, dataReceiver)
}
if ListClusters.NextToken != nil {
PaginationControl = ListClusters.NextToken
} else {
PaginationControl = nil
break
}
ClusterArns, err := sdk.CachedECSListClusters(m.ECSClient, aws.ToString(m.Caller.Account), region)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
return
}
for _, clusterARN := range ClusterArns {
m.getListTasks(clusterARN, region, dataReceiver)
}
}
func (m *ECSTasksModule) getListTasks(clusterARN string, region string, dataReceiver chan MappedECSTask) {
var PaginationControl *string
for {
ListTasks, err := m.ListTasksClient.ListTasks(
context.TODO(),
&(ecs.ListTasksInput{
Cluster: aws.String(clusterARN),
NextToken: PaginationControl,
}),
func(o *ecs.Options) {
o.Region = region
},
)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
break
}
batchSize := 100 // maximum value: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_DescribeTasks.html#API_DescribeTasks_RequestSyntax
for i := 0; i < len(ListTasks.TaskArns); i += batchSize {
j := i + batchSize
if j > len(ListTasks.TaskArns) {
j = len(ListTasks.TaskArns)
}
m.loadTasksData(clusterARN, ListTasks.TaskArns[i:j], region, dataReceiver)
}
if ListTasks.NextToken != nil {
PaginationControl = ListTasks.NextToken
} else {
PaginationControl = nil
break
}
TaskArns, err := sdk.CachedECSListTasks(m.ECSClient, aws.ToString(m.Caller.Account), region, clusterARN)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
return
}
batchSize := 100 // maximum value: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_DescribeTasks.html#API_DescribeTasks_RequestSyntax
for i := 0; i < len(TaskArns); i += batchSize {
j := i + batchSize
if j > len(TaskArns) {
j = len(TaskArns)
}
m.loadTasksData(clusterARN, TaskArns[i:j], region, dataReceiver)
}
}
func (m *ECSTasksModule) loadTasksData(clusterARN string, taskARNs []string, region string, dataReceiver chan MappedECSTask) {
@@ -390,16 +345,7 @@ func (m *ECSTasksModule) loadTasksData(clusterARN string, taskARNs []string, reg
return
}
DescribeTasks, err := m.DescribeTasksClient.DescribeTasks(
context.TODO(),
&(ecs.DescribeTasksInput{
Cluster: aws.String(clusterARN),
Tasks: taskARNs,
}),
func(o *ecs.Options) {
o.Region = region
},
)
Tasks, err := sdk.CachedECSDescribeTasks(m.ECSClient, aws.ToString(m.Caller.Account), region, clusterARN, taskARNs)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
@@ -407,7 +353,7 @@ func (m *ECSTasksModule) loadTasksData(clusterARN string, taskARNs []string, reg
}
eniIDs := []string{}
for _, task := range DescribeTasks.Tasks {
for _, task := range Tasks {
eniID := getElasticNetworkInterfaceIDOfECSTask(task)
if eniID != "" {
eniIDs = append(eniIDs, eniID)
@@ -420,8 +366,9 @@ func (m *ECSTasksModule) loadTasksData(clusterARN string, taskARNs []string, reg
return
}
for _, task := range DescribeTasks.Tasks {
taskDefinition, err := m.describeTaskDefinition(aws.ToString(task.TaskDefinitionArn), region)
for _, task := range Tasks {
//taskDefinition, err := m.describeTaskDefinition(aws.ToString(task.TaskDefinitionArn), region)
taskDefinition, err := sdk.CachedECSDescribeTaskDefinition(m.ECSClient, aws.ToString(m.Caller.Account), region, aws.ToString(task.TaskDefinitionArn))
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
@@ -461,7 +408,7 @@ func getTaskDefinitionContent(taskDefinition types.TaskDefinition) string {
}
func (m *ECSTasksModule) describeTaskDefinition(taskDefinitionArn string, region string) (types.TaskDefinition, error) {
DescribeTaskDefinition, err := m.DescribeTaskDefinitionClient.DescribeTaskDefinition(
DescribeTaskDefinition, err := m.ECSClient.DescribeTaskDefinition(
context.TODO(),
&ecs.DescribeTaskDefinitionInput{
TaskDefinition: &taskDefinitionArn,
@@ -511,20 +458,13 @@ func (m *ECSTasksModule) loadPublicIPs(eniIDs []string, region string) (map[stri
if len(eniIDs) == 0 {
return eniPublicIPs, nil
}
DescribeNetworkInterfaces, err := m.DescribeNetworkInterfacesClient.DescribeNetworkInterfaces(
context.TODO(),
&(ec2.DescribeNetworkInterfacesInput{
NetworkInterfaceIds: eniIDs,
}),
func(o *ec2.Options) {
o.Region = region
},
)
NetworkInterfaces, err := sdk.CachedEC2DescribeNetworkInterfaces(m.EC2Client, aws.ToString(m.Caller.Account), region)
if err != nil {
return nil, fmt.Errorf("getting elastic network interfaces: %s", err)
}
for _, eni := range DescribeNetworkInterfaces.NetworkInterfaces {
for _, eni := range NetworkInterfaces {
eniPublicIPs[aws.ToString(eni.NetworkInterfaceId)] = getPublicIPOfElasticNetworkInterface(eni)
}
+8 -246
View File
@@ -1,249 +1,15 @@
package aws
import (
"context"
"encoding/json"
"log"
"os"
"testing"
"time"
"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/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go-v2/service/ecs"
ecsTypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
const DESCRIBE_TASKS_TEST_FILE = "./test-data/describe-tasks.json"
const DESCRIBE_NETWORK_INTEFACES_TEST_FILE = "./test-data/describe-network-interfaces.json"
type ListTasks struct {
TaskArns []string `json:"taskArns"`
}
type DescribeTasks struct {
Tasks []struct {
Attachments []struct {
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
Details []struct {
Name string `json:"name"`
Value string `json:"value"`
} `json:"details"`
} `json:"attachments"`
Attributes []struct {
Name string `json:"name"`
Value string `json:"value"`
} `json:"attributes"`
AvailabilityZone string `json:"availabilityZone"`
ClusterArn string `json:"clusterArn"`
Connectivity string `json:"connectivity"`
ConnectivityAt string `json:"connectivityAt"`
Containers []struct {
ContainerArn string `json:"containerArn"`
TaskArn string `json:"taskArn"`
Name string `json:"name"`
Image string `json:"image"`
RuntimeID string `json:"runtimeId"`
LastStatus string `json:"lastStatus"`
NetworkBindings []interface{} `json:"networkBindings"`
NetworkInterfaces []struct {
AttachmentID string `json:"attachmentId"`
PrivateIpv4Address string `json:"privateIpv4Address"`
} `json:"networkInterfaces"`
HealthStatus string `json:"healthStatus"`
CPU string `json:"cpu"`
Memory string `json:"memory"`
} `json:"containers"`
CPU string `json:"cpu"`
CreatedAt string `json:"createdAt"`
DesiredStatus string `json:"desiredStatus"`
EnableExecuteCommand bool `json:"enableExecuteCommand"`
Group string `json:"group"`
HealthStatus string `json:"healthStatus"`
LastStatus string `json:"lastStatus"`
LaunchType string `json:"launchType"`
Memory string `json:"memory"`
Overrides struct {
ContainerOverrides []struct {
Name string `json:"name"`
} `json:"containerOverrides"`
InferenceAcceleratorOverrides []interface{} `json:"inferenceAcceleratorOverrides"`
} `json:"overrides"`
PlatformVersion string `json:"platformVersion"`
PlatformFamily string `json:"platformFamily"`
PullStartedAt string `json:"pullStartedAt"`
PullStoppedAt string `json:"pullStoppedAt"`
StartedAt string `json:"startedAt"`
StartedBy string `json:"startedBy"`
Tags []interface{} `json:"tags"`
TaskArn string `json:"taskArn"`
TaskDefinitionArn string `json:"taskDefinitionArn"`
Version int `json:"version"`
EphemeralStorage struct {
SizeInGiB int `json:"sizeInGiB"`
} `json:"ephemeralStorage"`
} `json:"tasks"`
Failures []interface{} `json:"failures"`
}
type DescribeNetworkInterfaces struct {
NetworkInterfaces []struct {
Status string `json:"Status"`
MacAddress string `json:"MacAddress"`
SourceDestCheck bool `json:"SourceDestCheck"`
VpcID string `json:"VpcId"`
Description string `json:"Description"`
Association struct {
PublicIP string `json:"PublicIp"`
AssociationID string `json:"AssociationId"`
PublicDNSName string `json:"PublicDnsName"`
IPOwnerID string `json:"IpOwnerId"`
} `json:"Association"`
NetworkInterfaceID string `json:"NetworkInterfaceId"`
PrivateIPAddresses []struct {
PrivateDNSName string `json:"PrivateDnsName"`
Association struct {
PublicIP string `json:"PublicIp"`
AssociationID string `json:"AssociationId"`
PublicDNSName string `json:"PublicDnsName"`
IPOwnerID string `json:"IpOwnerId"`
} `json:"Association"`
Primary bool `json:"Primary"`
PrivateIPAddress string `json:"PrivateIpAddress"`
} `json:"PrivateIpAddresses"`
RequesterManaged bool `json:"RequesterManaged"`
Ipv6Addresses []interface{} `json:"Ipv6Addresses"`
PrivateDNSName string `json:"PrivateDnsName,omitempty"`
AvailabilityZone string `json:"AvailabilityZone"`
Attachment struct {
Status string `json:"Status"`
DeviceIndex int `json:"DeviceIndex"`
AttachTime time.Time `json:"AttachTime"`
InstanceID string `json:"InstanceId"`
DeleteOnTermination bool `json:"DeleteOnTermination"`
AttachmentID string `json:"AttachmentId"`
InstanceOwnerID string `json:"InstanceOwnerId"`
} `json:"Attachment"`
Groups []struct {
GroupName string `json:"GroupName"`
GroupID string `json:"GroupId"`
} `json:"Groups"`
SubnetID string `json:"SubnetId"`
OwnerID string `json:"OwnerId"`
TagSet []interface{} `json:"TagSet"`
PrivateIPAddress string `json:"PrivateIpAddress"`
} `json:"NetworkInterfaces"`
}
func readTestFile(testFile string) []byte {
file, err := os.ReadFile(testFile)
if err != nil {
log.Fatalf("can't read file %s", testFile)
}
return file
}
type mockedListclustersClient struct {
}
func (c *mockedListclustersClient) ListClusters(context.Context, *ecs.ListClustersInput, ...func(*ecs.Options)) (*ecs.ListClustersOutput, error) {
return &ecs.ListClustersOutput{ClusterArns: []string{
"arn:aws:ecs:us-east-1:123456789012:cluster/MyCluster",
"arn:aws:ecs:us-east-1:123456789012:cluster/MyCluster2",
"arn:aws:ecs:us-east-1:123456789012:cluster/MyCluster3",
}}, nil
}
type mockedListTasksClient struct{}
func (c *mockedListTasksClient) ListTasks(ctx context.Context, input *ecs.ListTasksInput, f ...func(*ecs.Options)) (*ecs.ListTasksOutput, error) {
return &ecs.ListTasksOutput{TaskArns: []string{
"arn:aws:ecs:us-east-1:123456789012:task/MyCluster/74de0355a10a4f979ac495c14EXAMPLE",
"arn:aws:ecs:us-east-1:123456789012:task/MyCluster/d789e94343414c25b9f6bd59eEXAMPLE",
}}, nil
}
type mockedDescribeTasksClient struct {
describeTasks DescribeTasks
}
func (c *mockedDescribeTasksClient) DescribeTasks(ctx context.Context, input *ecs.DescribeTasksInput, f ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error) {
err := json.Unmarshal(readTestFile(DESCRIBE_TASKS_TEST_FILE), &c.describeTasks)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_TASKS_TEST_FILE)
}
var tasks []ecsTypes.Task
for _, mockedTask := range c.describeTasks.Tasks {
if mockedTask.ClusterArn == aws.ToString(input.Cluster) {
for _, inputTask := range input.Tasks {
if mockedTask.TaskArn == inputTask {
var attachments []ecsTypes.Attachment
for _, a := range mockedTask.Attachments {
var deets []ecsTypes.KeyValuePair
for _, detail := range a.Details {
deets = append(deets, ecsTypes.KeyValuePair{
Name: aws.String(detail.Name),
Value: aws.String(detail.Value)})
}
attachments = append(attachments, ecsTypes.Attachment{
Type: aws.String(a.Type),
Details: deets,
Id: aws.String(a.ID),
Status: aws.String(a.Status),
})
}
tasks = append(tasks, ecsTypes.Task{
ClusterArn: aws.String(mockedTask.ClusterArn),
TaskDefinitionArn: aws.String(mockedTask.TaskDefinitionArn),
LaunchType: ecsTypes.LaunchType(*aws.String(mockedTask.LaunchType)),
TaskArn: aws.String(mockedTask.TaskArn),
Attachments: attachments,
})
}
}
}
}
return &ecs.DescribeTasksOutput{Tasks: tasks}, nil
}
type mockedDescribeNetworkInterfacesClient struct {
describeNetworkInterfaces DescribeNetworkInterfaces
}
func (c *mockedDescribeNetworkInterfacesClient) DescribeNetworkInterfaces(ctx context.Context, input *ec2.DescribeNetworkInterfacesInput, f ...func(o *ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) {
var nics []ec2types.NetworkInterface
err := json.Unmarshal(readTestFile(DESCRIBE_NETWORK_INTEFACES_TEST_FILE), &c.describeNetworkInterfaces)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_NETWORK_INTEFACES_TEST_FILE)
}
for _, mockedNic := range c.describeNetworkInterfaces.NetworkInterfaces {
for _, inputNicID := range input.NetworkInterfaceIds {
if mockedNic.NetworkInterfaceID == inputNicID {
nics = append(nics, ec2types.NetworkInterface{
Association: &ec2types.NetworkInterfaceAssociation{
PublicIp: aws.String(mockedNic.Association.PublicIP),
},
NetworkInterfaceId: aws.String(mockedNic.NetworkInterfaceID)})
}
}
}
return &ec2.DescribeNetworkInterfacesOutput{NetworkInterfaces: nics}, nil
}
type mockedDescribeTaskDefinitionInterface struct {
}
func (c *mockedDescribeTaskDefinitionInterface) DescribeTaskDefinition(ctx context.Context, input *ecs.DescribeTaskDefinitionInput, f ...func(o *ecs.Options)) (*ecs.DescribeTaskDefinitionOutput, error) {
testTaskDefinition := ecsTypes.TaskDefinition{}
testTaskDefinition.TaskRoleArn = aws.String("test123")
return &ecs.DescribeTaskDefinitionOutput{TaskDefinition: &testTaskDefinition}, nil
}
func TestECSTasks(t *testing.T) {
subtests := []struct {
name string
@@ -258,17 +24,13 @@ func TestECSTasks(t *testing.T) {
verbosity: 2,
testModule: ECSTasksModule{
AWSProfile: "default",
AWSRegions: []string{"us-east-1", "us-west-1"},
Caller: sts.GetCallerIdentityOutput{Arn: aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests")},
SkipAdminCheck: true,
Goroutines: 30,
DescribeNetworkInterfacesClient: &mockedDescribeNetworkInterfacesClient{},
DescribeTasksClient: &mockedDescribeTasksClient{},
ListTasksClient: &mockedListTasksClient{},
ListClustersClient: &mockedListclustersClient{},
DescribeTaskDefinitionClient: &mockedDescribeTaskDefinitionInterface{},
//IAMSimulatePrincipalPolicyClient: &mockedDescribeTaskDefinitionsClient{},
AWSProfile: "default",
AWSRegions: []string{"us-east-1", "us-west-1"},
Caller: sts.GetCallerIdentityOutput{Arn: aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests")},
SkipAdminCheck: true,
Goroutines: 30,
EC2Client: &sdk.MockedEC2Client2{},
ECSClient: &sdk.MockedECSClient{},
},
expectedResult: []MappedECSTask{{
Cluster: "MyCluster",
+38 -45
View File
@@ -1,16 +1,15 @@
package aws
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"sync"
"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/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/bishopfox/awsservicemap"
@@ -18,8 +17,7 @@ import (
)
type ElasticNetworkInterfacesModule struct {
//EC2Client *ec2.Client
DescribeNetworkInterfacesClient ec2.DescribeNetworkInterfacesAPIClient
EC2Client sdk.AWSEC2ClientInterface
Caller sts.GetCallerIdentityOutput
AWSRegions []string
@@ -213,50 +211,45 @@ func (m *ElasticNetworkInterfacesModule) executeChecks(r string, wg *sync.WaitGr
}
func (m *ElasticNetworkInterfacesModule) getDescribeNetworkInterfaces(region string, dataReceiver chan MappedENI) {
var PaginationControl *string
for {
DescribeNetworkInterfaces, err := m.DescribeNetworkInterfacesClient.DescribeNetworkInterfaces(
context.TODO(),
&(ec2.DescribeNetworkInterfacesInput{
NextToken: PaginationControl,
}),
func(o *ec2.Options) {
o.Region = region
},
)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
break
}
for _, eni := range DescribeNetworkInterfaces.NetworkInterfaces {
status := string(eni.Status)
if status == "available" {
continue // unused ENI
}
mappedENI := MappedENI{
ID: aws.ToString(eni.NetworkInterfaceId),
Type: string(eni.InterfaceType),
ExternalIP: getPublicIPOfElasticNetworkInterface(eni),
PrivateIP: aws.ToString(eni.PrivateIpAddress),
VPCID: aws.ToString(eni.VpcId),
AttachedInstance: getAttachmentInstanceOfElasticNetworkInterface(eni),
Description: aws.ToString(eni.Description),
}
dataReceiver <- mappedENI
}
if DescribeNetworkInterfaces.NextToken != nil {
PaginationControl = DescribeNetworkInterfaces.NextToken
} else {
PaginationControl = nil
break
}
NetworkInterfaces, err := sdk.CachedEC2DescribeNetworkInterfaces(m.EC2Client, aws.ToString(m.Caller.Account), region)
// var PaginationControl *string
// for {
// DescribeNetworkInterfaces, err := m.DescribeNetworkInterfacesClient.DescribeNetworkInterfaces(
// context.TODO(),
// &(ec2.DescribeNetworkInterfacesInput{
// NextToken: PaginationControl,
// }),
// func(o *ec2.Options) {
// o.Region = region
// },
// )
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
return
}
for _, eni := range NetworkInterfaces {
status := string(eni.Status)
if status == "available" {
continue // unused ENI
}
mappedENI := MappedENI{
ID: aws.ToString(eni.NetworkInterfaceId),
Type: string(eni.InterfaceType),
ExternalIP: getPublicIPOfElasticNetworkInterface(eni),
PrivateIP: aws.ToString(eni.PrivateIpAddress),
VPCID: aws.ToString(eni.VpcId),
AttachedInstance: getAttachmentInstanceOfElasticNetworkInterface(eni),
Description: aws.ToString(eni.Description),
}
dataReceiver <- mappedENI
}
}
func getPublicIPOfElasticNetworkInterface(elasticNetworkInterface types.NetworkInterface) string {
+44 -33
View File
@@ -1,48 +1,59 @@
package aws
import (
"context"
"encoding/json"
"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/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
type mockedDescribeNetworkInterfacesClient2 struct {
describeNetworkInterfaces DescribeNetworkInterfaces
}
func TestElasticNetworkInterfaces(t *testing.T) {
func (c *mockedDescribeNetworkInterfacesClient2) DescribeNetworkInterfaces(ctx context.Context, input *ec2.DescribeNetworkInterfacesInput, f ...func(o *ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) {
var nics []ec2types.NetworkInterface
err := json.Unmarshal(readTestFile(DESCRIBE_NETWORK_INTEFACES_TEST_FILE), &c.describeNetworkInterfaces)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_NETWORK_INTEFACES_TEST_FILE)
m := ElasticNetworkInterfacesModule{
AWSProfile: "default",
AWSRegions: []string{"us-east-1", "us-west-1"},
Caller: sts.GetCallerIdentityOutput{Arn: aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests")},
EC2Client: &sdk.MockedEC2Client2{},
}
for _, mockednic := range c.describeNetworkInterfaces.NetworkInterfaces {
nics = append(nics, ec2types.NetworkInterface{
Association: &ec2types.NetworkInterfaceAssociation{
PublicIp: aws.String(mockednic.Association.PublicIP),
//m.ElasticNetworkInterfaces("table", ".", 3)
subtests := []struct {
name string
testModule ElasticNetworkInterfacesModule
expectedResult []MappedENI
}{
{
name: "Test ElasticNetworkInterfaces",
testModule: m,
expectedResult: []MappedENI{
{
PrivateIP: "10.0.1.17",
ExternalIP: "203.0.113.12",
},
{
PrivateIP: "10.0.1.149",
ExternalIP: "198.51.100.0",
},
},
NetworkInterfaceId: aws.String(mockednic.NetworkInterfaceID),
PrivateIpAddress: aws.String(mockednic.PrivateIPAddress),
VpcId: aws.String(mockednic.VpcID),
Attachment: &ec2types.NetworkInterfaceAttachment{InstanceId: aws.String(mockednic.Attachment.InstanceID)},
Description: aws.String(mockednic.Description),
},
}
internal.MockFileSystem(true)
for _, subtest := range subtests {
t.Run(subtest.name, func(t *testing.T) {
subtest.testModule.ElasticNetworkInterfaces("table", ".", 3)
for index, expectedTask := range subtest.expectedResult {
if expectedTask.ExternalIP != subtest.testModule.MappedENIs[index].ExternalIP {
t.Errorf("expected %s, got %s", expectedTask.ExternalIP, subtest.testModule.MappedENIs[index].ExternalIP)
}
if expectedTask.PrivateIP != subtest.testModule.MappedENIs[index].PrivateIP {
t.Errorf("expected %s, got %s", expectedTask.PrivateIP, subtest.testModule.MappedENIs[index].PrivateIP)
}
}
})
}
return &ec2.DescribeNetworkInterfacesOutput{NetworkInterfaces: nics}, nil
}
func TestElasticNetworkInterfaces(t *testing.T) {
m := ElasticNetworkInterfacesModule{
AWSProfile: "default",
AWSRegions: []string{"us-east-1", "us-west-1"},
Caller: sts.GetCallerIdentityOutput{Arn: aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests")},
DescribeNetworkInterfacesClient: &mockedDescribeNetworkInterfacesClient2{},
}
m.ElasticNetworkInterfaces("table", ".", 3)
}
+1805 -601
View File
File diff suppressed because it is too large. Load diff
+36 -57
View File
@@ -1,22 +1,21 @@
package aws
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"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/route53"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/sirupsen/logrus"
)
type Route53Module struct {
// General configuration data
Route53Client *route53.Client
Route53Client sdk.AWSRoute53ClientInterface
Caller sts.GetCallerIdentityOutput
AWSRegions []string
@@ -127,7 +126,7 @@ func (m *Route53Module) writeLoot(outputDirectory string, verbosity int) {
var route53APublicRecords string
for _, record := range m.Records {
if record.Type == "A" || record.Type == "AAAA" {
if record.Type == "A" || record.Type == "AAAA" || record.Type == "CNAME" {
if record.PrivateZone == "True" {
route53APrivateRecords = route53APrivateRecords + fmt.Sprintln(record.Name)
} else {
@@ -176,71 +175,51 @@ func (m *Route53Module) writeLoot(outputDirectory string, verbosity int) {
func (m *Route53Module) getRoute53Records() {
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
var PaginationControl *string
var recordName string
var recordType string
for {
ListHostedZones, err := m.Route53Client.ListHostedZones(
context.TODO(),
&route53.ListHostedZonesInput{
Marker: PaginationControl,
},
)
HostedZones, err := sdk.CachedRoute53ListHostedZones(m.Route53Client, aws.ToString(m.Caller.Account))
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
return
}
var privateZone string
for _, zone := range HostedZones {
if zone.Config.PrivateZone {
privateZone = "True"
} else {
privateZone = "False"
}
Records, err := sdk.CachedRoute53ListResourceRecordSets(m.Route53Client, aws.ToString(m.Caller.Account), aws.ToString(zone.Id))
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
break
}
for _, record := range Records {
recordName = aws.ToString(record.Name)
recordType = string(record.Type)
for _, resourceRecord := range record.ResourceRecords {
recordValue := resourceRecord.Value
m.Records = append(
m.Records,
Record{
AWSService: "Route53",
Name: recordName,
Type: recordType,
Value: aws.ToString(recordValue),
PrivateZone: privateZone,
})
var privateZone string
for _, zone := range ListHostedZones.HostedZones {
id := aws.ToString(zone.Id)
if zone.Config.PrivateZone {
privateZone = "True"
} else {
privateZone = "False"
}
ListResourceRecordSets, err := m.Route53Client.ListResourceRecordSets(
context.TODO(),
&route53.ListResourceRecordSetsInput{
HostedZoneId: &id,
},
)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
break
}
for _, record := range ListResourceRecordSets.ResourceRecordSets {
recordName = aws.ToString(record.Name)
recordType = string(record.Type)
for _, resourceRecord := range record.ResourceRecords {
recordValue := resourceRecord.Value
m.Records = append(
m.Records,
Record{
AWSService: "Route53",
Name: recordName,
Type: recordType,
Value: aws.ToString(recordValue),
PrivateZone: privateZone,
})
}
}
}
// The "NextToken" value is nil when there's no more data to return.
if ListHostedZones.NextMarker != nil {
PaginationControl = ListHostedZones.NextMarker
} else {
PaginationControl = nil
break
}
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ type APIGatewayClientInterface interface {
GetRestApis(context.Context, *apigateway.GetRestApisInput, ...func(*apigateway.Options)) (*apigateway.GetRestApisOutput, error)
}
func RegisterApiGatewayTypes() {
func init() {
gob.Register([]apiGatewayTypes.RestApi{})
}
+27
View File
@@ -0,0 +1,27 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/apigateway"
apiGatewayTypes "github.com/aws/aws-sdk-go-v2/service/apigateway/types"
)
type MockedAWSAPIGatewayClient struct {
}
func (m *MockedAWSAPIGatewayClient) GetRestApis(ctx context.Context, input *apigateway.GetRestApisInput, options ...func(*apigateway.Options)) (*apigateway.GetRestApisOutput, error) {
return &apigateway.GetRestApisOutput{
Items: []apiGatewayTypes.RestApi{
{
Id: aws.String("api1"),
Name: aws.String("api1"),
},
{
Id: aws.String("api2"),
Name: aws.String("api2"),
},
},
}, nil
}
+1 -1
View File
@@ -16,7 +16,7 @@ type APIGatewayv2ClientInterface interface {
GetApis(context.Context, *apigatewayv2.GetApisInput, ...func(*apigatewayv2.Options)) (*apigatewayv2.GetApisOutput, error)
}
func RegisterApiGatewayV2Types() {
func init() {
gob.Register([]apiGatwayV2Types.Api{})
}
+27
View File
@@ -0,0 +1,27 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/apigatewayv2"
apiGatwayV2Types "github.com/aws/aws-sdk-go-v2/service/apigatewayv2/types"
)
type MockedAWSAPIGatewayv2Client struct {
}
func (m *MockedAWSAPIGatewayv2Client) GetApis(ctx context.Context, input *apigatewayv2.GetApisInput, options ...func(*apigatewayv2.Options)) (*apigatewayv2.GetApisOutput, error) {
return &apigatewayv2.GetApisOutput{
Items: []apiGatwayV2Types.Api{
{
ApiId: aws.String("api1"),
Name: aws.String("api1"),
},
{
ApiId: aws.String("api2"),
Name: aws.String("api2"),
},
},
}, nil
}
+1 -1
View File
@@ -15,7 +15,7 @@ type AppRunnerClientInterface interface {
ListServices(context.Context, *apprunner.ListServicesInput, ...func(*apprunner.Options)) (*apprunner.ListServicesOutput, error)
}
func RegisterApprunnerTypes() {
func init() {
gob.Register([]apprunnerTypes.Service{})
gob.Register([]apprunnerTypes.ServiceSummary{})
}
+25
View File
@@ -0,0 +1,25 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/apprunner"
apprunnerTypes "github.com/aws/aws-sdk-go-v2/service/apprunner/types"
)
type MockedAppRunnerClient struct {
}
func (m *MockedAppRunnerClient) ListServices(ctx context.Context, input *apprunner.ListServicesInput, options ...func(*apprunner.Options)) (*apprunner.ListServicesOutput, error) {
return &apprunner.ListServicesOutput{
ServiceSummaryList: []apprunnerTypes.ServiceSummary{
{
ServiceName: aws.String("service1"),
},
{
ServiceName: aws.String("service2"),
},
},
}, nil
}
+96
View File
@@ -0,0 +1,96 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/athena"
athenaTypes "github.com/aws/aws-sdk-go-v2/service/athena/types"
"github.com/patrickmn/go-cache"
)
type AWSAthenaClientInterface interface {
ListDataCatalogs(context.Context, *athena.ListDataCatalogsInput, ...func(*athena.Options)) (*athena.ListDataCatalogsOutput, error)
ListDatabases(context.Context, *athena.ListDatabasesInput, ...func(*athena.Options)) (*athena.ListDatabasesOutput, error)
}
func init() {
gob.Register([]athenaTypes.DataCatalogSummary{})
}
func CachedAthenaListDataCatalogs(client AWSAthenaClientInterface, accountID string, region string) ([]athenaTypes.DataCatalogSummary, error) {
var PaginationControl *string
var dataCatalogs []athenaTypes.DataCatalogSummary
cacheKey := fmt.Sprintf("%s-athena-ListDataCatalogs-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]athenaTypes.DataCatalogSummary), nil
}
for {
ListDataCatalogs, err := client.ListDataCatalogs(
context.TODO(),
&athena.ListDataCatalogsInput{
NextToken: PaginationControl,
},
func(o *athena.Options) {
o.Region = region
},
)
if err != nil {
return dataCatalogs, err
}
dataCatalogs = append(dataCatalogs, ListDataCatalogs.DataCatalogsSummary...)
//pagination
if ListDataCatalogs.NextToken == nil {
break
}
PaginationControl = ListDataCatalogs.NextToken
}
internal.Cache.Set(cacheKey, dataCatalogs, cache.DefaultExpiration)
return dataCatalogs, nil
}
func CachedAthenaListDatabases(client AWSAthenaClientInterface, accountID string, region string, catalogName string) ([]string, error) {
var PaginationControl *string
var databases []string
cacheKey := fmt.Sprintf("%s-athena-ListDatabases-%s-%s", accountID, region, catalogName)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]string), nil
}
for {
ListDatabases, err := client.ListDatabases(
context.TODO(),
&athena.ListDatabasesInput{
CatalogName: &catalogName,
NextToken: PaginationControl,
},
func(o *athena.Options) {
o.Region = region
},
)
if err != nil {
return databases, err
}
for _, database := range ListDatabases.DatabaseList {
databases = append(databases, *database.Name)
}
//pagination
if ListDatabases.NextToken == nil {
break
}
PaginationControl = ListDatabases.NextToken
}
internal.Cache.Set(cacheKey, databases, cache.DefaultExpiration)
return databases, nil
}
+38
View File
@@ -0,0 +1,38 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/athena"
athenaTypes "github.com/aws/aws-sdk-go-v2/service/athena/types"
)
type MockedAWSAthenaClient struct {
}
func (m *MockedAWSAthenaClient) ListDatabases(ctx context.Context, input *athena.ListDatabasesInput, options ...func(*athena.Options)) (*athena.ListDatabasesOutput, error) {
return &athena.ListDatabasesOutput{
DatabaseList: []athenaTypes.Database{
{
Name: aws.String("db1"),
},
{
Name: aws.String("db2"),
},
},
}, nil
}
func (m *MockedAWSAthenaClient) ListDataCatalogs(ctx context.Context, input *athena.ListDataCatalogsInput, options ...func(*athena.Options)) (*athena.ListDataCatalogsOutput, error) {
return &athena.ListDataCatalogsOutput{
DataCatalogsSummary: []athenaTypes.DataCatalogSummary{
{
CatalogName: aws.String("catalog1"),
},
{
CatalogName: aws.String("catalog2"),
},
},
}, nil
}
+1 -1
View File
@@ -18,7 +18,7 @@ type CloudFormationClientInterface interface {
ListStacks(context.Context, *cloudformation.ListStacksInput, ...func(*cloudformation.Options)) (*cloudformation.ListStacksOutput, error)
}
func RegisterCloudFormationTypes() {
func init() {
gob.Register([]cloudFormationTypes.Stack{})
gob.Register([]cloudFormationTypes.StackSummary{})
}
+73
View File
@@ -0,0 +1,73 @@
package sdk
import (
"context"
"encoding/json"
"log"
"time"
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
"github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
)
const DESCRIBE_STACKS_TEST_FILE = "./test-data/cloudformation-describestacks.json"
const TEMPLATE_BODY_TEST_FILE = "./test-data/cloudformation-getTemplate.json"
type Stacks struct {
Stacks []struct {
StackID string `json:"StackId"`
Description string `json:"Description"`
Tags []interface{} `json:"Tags"`
Outputs []types.Output `json:"Outputs"`
Parameters []types.Parameter `json:"Parameters"`
StackStatusReason interface{} `json:"StackStatusReason"`
CreationTime time.Time `json:"CreationTime"`
Capabilities []interface{} `json:"Capabilities"`
StackName string `json:"StackName"`
RoleArn string `json:"RoleArn"`
StackStatus string `json:"StackStatus"`
DisableRollback bool `json:"DisableRollback"`
} `json:"Stacks"`
}
type TemplateBody struct {
TemplateBody string `json:"TemplateBody"`
}
type MockedCloudformationClient struct {
describeStacks Stacks
getTemplateBody TemplateBody
}
func (m *MockedCloudformationClient) DescribeStacks(ctx context.Context, params *cloudformation.DescribeStacksInput, optFns ...func(*cloudformation.Options)) (*cloudformation.DescribeStacksOutput, error) {
err := json.Unmarshal(readTestFile(DESCRIBE_STACKS_TEST_FILE), &m.describeStacks)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_STACKS_TEST_FILE)
}
var stacks []types.Stack
for _, stack := range m.describeStacks.Stacks {
stacks = append(stacks, types.Stack{
StackName: &stack.StackName,
RoleARN: &stack.RoleArn,
Outputs: stack.Outputs,
Parameters: stack.Parameters,
})
}
return &cloudformation.DescribeStacksOutput{Stacks: stacks}, nil
}
func (m *MockedCloudformationClient) GetTemplate(ctx context.Context, params *cloudformation.GetTemplateInput, optFns ...func(*cloudformation.Options)) (*cloudformation.GetTemplateOutput, error) {
err := json.Unmarshal(readTestFile(DESCRIBE_STACKS_TEST_FILE), &m.getTemplateBody)
if err != nil {
log.Fatalf("can't unmarshall file %s", TEMPLATE_BODY_TEST_FILE)
}
return &cloudformation.GetTemplateOutput{TemplateBody: &m.getTemplateBody.TemplateBody}, nil
}
func (m *MockedCloudformationClient) ListStacks(ctx context.Context, params *cloudformation.ListStacksInput, optFns ...func(*cloudformation.Options)) (*cloudformation.ListStacksOutput, error) {
return &cloudformation.ListStacksOutput{}, nil
}
+57
View File
@@ -0,0 +1,57 @@
package sdk
import (
"context"
"encoding/gob"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/cloudfront"
cloudfrontTypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
"github.com/patrickmn/go-cache"
)
type AWSCloudFrontClientInterface interface {
ListDistributions(ctx context.Context, params *cloudfront.ListDistributionsInput, optFns ...func(*cloudfront.Options)) (*cloudfront.ListDistributionsOutput, error)
}
func init() {
gob.Register([]cloudfrontTypes.DistributionSummary{})
gob.Register(cloudfrontTypes.DistributionSummary{})
}
func CachedCloudFrontListDistributions(CloudFrontClient AWSCloudFrontClientInterface, accountID string) ([]cloudfrontTypes.DistributionSummary, error) {
var PaginationControl *string
var distributions []cloudfrontTypes.DistributionSummary
cacheKey := "cloudfront-ListDistributions-" + accountID
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached CloudFront distributions data")
return cached.([]cloudfrontTypes.DistributionSummary), nil
}
for {
ListDistributions, err := CloudFrontClient.ListDistributions(
context.TODO(),
&cloudfront.ListDistributionsInput{
Marker: PaginationControl,
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
distributions = append(distributions, ListDistributions.DistributionList.Items...)
// Pagination control.
if ListDistributions.DistributionList.NextMarker != nil {
PaginationControl = ListDistributions.DistributionList.NextMarker
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, distributions, cache.DefaultExpiration)
return distributions, nil
}
+27
View File
@@ -0,0 +1,27 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudfront"
cloudfrontTypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
)
type MockedAWSCloudFrontClient struct {
}
func (m *MockedAWSCloudFrontClient) ListDistributions(ctx context.Context, input *cloudfront.ListDistributionsInput, options ...func(*cloudfront.Options)) (*cloudfront.ListDistributionsOutput, error) {
return &cloudfront.ListDistributionsOutput{
DistributionList: &cloudfrontTypes.DistributionList{
Items: []cloudfrontTypes.DistributionSummary{
{
Id: aws.String("distribution1"),
},
{
Id: aws.String("distribution2"),
},
},
},
}, nil
}
+94
View File
@@ -0,0 +1,94 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/codeartifact"
codeArtifactTypes "github.com/aws/aws-sdk-go-v2/service/codeartifact/types"
"github.com/patrickmn/go-cache"
)
type AWSCodeArtifactClientInterface interface {
ListDomains(context.Context, *codeartifact.ListDomainsInput, ...func(*codeartifact.Options)) (*codeartifact.ListDomainsOutput, error)
ListRepositories(context.Context, *codeartifact.ListRepositoriesInput, ...func(*codeartifact.Options)) (*codeartifact.ListRepositoriesOutput, error)
}
func init() {
gob.Register([]codeArtifactTypes.DomainSummary{})
gob.Register([]codeArtifactTypes.RepositorySummary{})
}
func CachedCodeArtifactListDomains(client AWSCodeArtifactClientInterface, accountID string, region string) ([]codeArtifactTypes.DomainSummary, error) {
var PaginationControl *string
var domains []codeArtifactTypes.DomainSummary
cacheKey := fmt.Sprintf("%s-codeartifact-ListDomains-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]codeArtifactTypes.DomainSummary), nil
}
for {
ListDomains, err := client.ListDomains(
context.TODO(),
&codeartifact.ListDomainsInput{
NextToken: PaginationControl,
},
func(o *codeartifact.Options) {
o.Region = region
},
)
if err != nil {
return domains, err
}
domains = append(domains, ListDomains.Domains...)
//pagination
if ListDomains.NextToken == nil {
break
}
PaginationControl = ListDomains.NextToken
}
internal.Cache.Set(cacheKey, domains, cache.DefaultExpiration)
return domains, nil
}
func CachedCodeArtifactListRepositories(client AWSCodeArtifactClientInterface, accountID string, region string) ([]codeArtifactTypes.RepositorySummary, error) {
var PaginationControl *string
var repositories []codeArtifactTypes.RepositorySummary
cacheKey := fmt.Sprintf("%s-codeartifact-ListRepositories-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]codeArtifactTypes.RepositorySummary), nil
}
for {
ListRepositories, err := client.ListRepositories(
context.TODO(),
&codeartifact.ListRepositoriesInput{
NextToken: PaginationControl,
},
func(o *codeartifact.Options) {
o.Region = region
},
)
if err != nil {
return repositories, err
}
repositories = append(repositories, ListRepositories.Repositories...)
//pagination
if ListRepositories.NextToken == nil {
break
}
PaginationControl = ListRepositories.NextToken
}
internal.Cache.Set(cacheKey, repositories, cache.DefaultExpiration)
return repositories, nil
}
+46
View File
@@ -0,0 +1,46 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/codeartifact"
codeArtifactTypes "github.com/aws/aws-sdk-go-v2/service/codeartifact/types"
)
type MockedAWSCodeArtifactClient struct {
}
func (m *MockedAWSCodeArtifactClient) ListRepositories(ctx context.Context, input *codeartifact.ListRepositoriesInput, options ...func(*codeartifact.Options)) (*codeartifact.ListRepositoriesOutput, error) {
return &codeartifact.ListRepositoriesOutput{
NextToken: nil,
Repositories: []codeArtifactTypes.RepositorySummary{
{
Name: aws.String("repo1"),
Arn: aws.String("arn:aws:codeartifact:us-east-1:123456789012:repository/repo1"),
DomainName: aws.String("domain1"),
},
{
Name: aws.String("repo2"),
Arn: aws.String("arn:aws:codeartifact:us-east-1:123456789012:repository/repo2"),
DomainName: aws.String("domain1"),
},
},
}, nil
}
func (m *MockedAWSCodeArtifactClient) ListDomains(ctx context.Context, input *codeartifact.ListDomainsInput, options ...func(*codeartifact.Options)) (*codeartifact.ListDomainsOutput, error) {
return &codeartifact.ListDomainsOutput{
NextToken: nil,
Domains: []codeArtifactTypes.DomainSummary{
{
Name: aws.String("domain1"),
Arn: aws.String("arn:aws:codeartifact:us-east-1:123456789012:domain/domain1"),
},
{
Name: aws.String("domain2"),
Arn: aws.String("arn:aws:codeartifact:us-east-1:123456789012:domain/domain2"),
},
},
}, nil
}
+1 -1
View File
@@ -18,7 +18,7 @@ type CodeBuildClientInterface interface {
GetResourcePolicy(ctx context.Context, params *codebuild.GetResourcePolicyInput, optFns ...func(*codebuild.Options)) (*codebuild.GetResourcePolicyOutput, error)
}
func RegisterCodeBuildTypes() {
func init() {
gob.Register(codeBuildTypes.Project{})
}
+19
View File
@@ -0,0 +1,19 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/service/codebuild"
)
type MockedCodeBuildClient struct {
}
func (m *MockedCodeBuildClient) ListProjects(ctx context.Context, input *codebuild.ListProjectsInput, options ...func(*codebuild.Options)) (*codebuild.ListProjectsOutput, error) {
return &codebuild.ListProjectsOutput{
Projects: []string{
"project1",
"project2",
},
}, nil
}
+56
View File
@@ -0,0 +1,56 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/codecommit"
codeCommitTypes "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
"github.com/patrickmn/go-cache"
)
type AWSCodeCommitClientInterface interface {
ListRepositories(context.Context, *codecommit.ListRepositoriesInput, ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error)
}
func init() {
gob.Register([]codeCommitTypes.RepositoryNameIdPair{})
}
func CachedCodeCommitListRepositories(client AWSCodeCommitClientInterface, accountID string, region string) ([]codeCommitTypes.RepositoryNameIdPair, error) {
var PaginationControl *string
var repositories []codeCommitTypes.RepositoryNameIdPair
cacheKey := fmt.Sprintf("%s-codecommit-ListRepositories-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]codeCommitTypes.RepositoryNameIdPair), nil
}
for {
ListRepositories, err := client.ListRepositories(
context.TODO(),
&codecommit.ListRepositoriesInput{
NextToken: PaginationControl,
},
func(o *codecommit.Options) {
o.Region = region
},
)
if err != nil {
return repositories, err
}
repositories = append(repositories, ListRepositories.Repositories...)
//pagination
if ListRepositories.NextToken == nil {
break
}
PaginationControl = ListRepositories.NextToken
}
internal.Cache.Set(cacheKey, repositories, cache.DefaultExpiration)
return repositories, nil
}
+30
View File
@@ -0,0 +1,30 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/codecommit"
codeCommitTypes "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
)
// create mocks for codecommit ListRepositories
type MockedAWSCodeCommitClient struct {
}
func (m *MockedAWSCodeCommitClient) ListRepositories(ctx context.Context, input *codecommit.ListRepositoriesInput, options ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error) {
return &codecommit.ListRepositoriesOutput{
NextToken: nil,
Repositories: []codeCommitTypes.RepositoryNameIdPair{
{
RepositoryId: aws.String("repo1"),
RepositoryName: aws.String("repo1"),
},
{
RepositoryId: aws.String("repo2"),
RepositoryName: aws.String("repo2"),
},
},
}, nil
}
+212
View File
@@ -0,0 +1,212 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/codedeploy"
codeDeployTypes "github.com/aws/aws-sdk-go-v2/service/codedeploy/types"
"github.com/patrickmn/go-cache"
)
type AWSCodeDeployClientInterface interface {
ListApplications(context.Context, *codedeploy.ListApplicationsInput, ...func(*codedeploy.Options)) (*codedeploy.ListApplicationsOutput, error)
GetApplication(context.Context, *codedeploy.GetApplicationInput, ...func(*codedeploy.Options)) (*codedeploy.GetApplicationOutput, error)
ListDeployments(context.Context, *codedeploy.ListDeploymentsInput, ...func(*codedeploy.Options)) (*codedeploy.ListDeploymentsOutput, error)
GetDeployment(context.Context, *codedeploy.GetDeploymentInput, ...func(*codedeploy.Options)) (*codedeploy.GetDeploymentOutput, error)
ListDeploymentConfigs(context.Context, *codedeploy.ListDeploymentConfigsInput, ...func(*codedeploy.Options)) (*codedeploy.ListDeploymentConfigsOutput, error)
GetDeploymentConfig(context.Context, *codedeploy.GetDeploymentConfigInput, ...func(*codedeploy.Options)) (*codedeploy.GetDeploymentConfigOutput, error)
}
func init() {
gob.Register([]string{})
gob.Register([]codeDeployTypes.ApplicationInfo{})
gob.Register([]codeDeployTypes.DeploymentConfigInfo{})
gob.Register([]codeDeployTypes.DeploymentInfo{})
}
func CachedCodeDeployListApplications(client AWSCodeDeployClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var applications []string
cacheKey := fmt.Sprintf("%s-codedeploy-ListApplications-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]string), nil
}
for {
ListApplications, err := client.ListApplications(
context.TODO(),
&codedeploy.ListApplicationsInput{
NextToken: PaginationControl,
},
func(o *codedeploy.Options) {
o.Region = region
},
)
if err != nil {
return applications, err
}
applications = append(applications, ListApplications.Applications...)
//pagination
if ListApplications.NextToken == nil {
break
}
PaginationControl = ListApplications.NextToken
}
internal.Cache.Set(cacheKey, applications, cache.DefaultExpiration)
return applications, nil
}
func CachedCodeDeployListDeploymentConfigs(client AWSCodeDeployClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var deploymentConfigs []string
cacheKey := fmt.Sprintf("%s-codedeploy-ListDeploymentConfigs-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]string), nil
}
for {
ListDeploymentConfigs, err := client.ListDeploymentConfigs(
context.TODO(),
&codedeploy.ListDeploymentConfigsInput{
NextToken: PaginationControl,
},
func(o *codedeploy.Options) {
o.Region = region
},
)
if err != nil {
return deploymentConfigs, err
}
deploymentConfigs = append(deploymentConfigs, ListDeploymentConfigs.DeploymentConfigsList...)
//pagination
if ListDeploymentConfigs.NextToken == nil {
break
}
PaginationControl = ListDeploymentConfigs.NextToken
}
internal.Cache.Set(cacheKey, deploymentConfigs, cache.DefaultExpiration)
return deploymentConfigs, nil
}
func CachedCodeDeployListDeployments(client AWSCodeDeployClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var deployments []string
cacheKey := fmt.Sprintf("%s-codedeploy-ListDeployments-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]string), nil
}
for {
ListDeployments, err := client.ListDeployments(
context.TODO(),
&codedeploy.ListDeploymentsInput{
NextToken: PaginationControl,
},
func(o *codedeploy.Options) {
o.Region = region
},
)
if err != nil {
return deployments, err
}
deployments = append(deployments, ListDeployments.Deployments...)
//pagination
if ListDeployments.NextToken == nil {
break
}
PaginationControl = ListDeployments.NextToken
}
internal.Cache.Set(cacheKey, deployments, cache.DefaultExpiration)
return deployments, nil
}
func CachedCodeDeployGetApplication(client AWSCodeDeployClientInterface, accountID string, region string, applicationName string) (codeDeployTypes.ApplicationInfo, error) {
cacheKey := fmt.Sprintf("%s-codedeploy-GetApplication-%s-%s", accountID, region, applicationName)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.(codeDeployTypes.ApplicationInfo), nil
}
GetApplication, err := client.GetApplication(
context.TODO(),
&codedeploy.GetApplicationInput{
ApplicationName: &applicationName,
},
func(o *codedeploy.Options) {
o.Region = region
},
)
if err != nil {
return *GetApplication.Application, err
}
internal.Cache.Set(cacheKey, *GetApplication.Application, cache.DefaultExpiration)
return *GetApplication.Application, nil
}
func CachedCodeDeployGetDeploymentConfig(client AWSCodeDeployClientInterface, accountID string, region string, deploymentConfigName string) (codeDeployTypes.DeploymentConfigInfo, error) {
cacheKey := fmt.Sprintf("%s-codedeploy-GetDeploymentConfig-%s-%s", accountID, region, deploymentConfigName)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.(codeDeployTypes.DeploymentConfigInfo), nil
}
GetDeploymentConfig, err := client.GetDeploymentConfig(
context.TODO(),
&codedeploy.GetDeploymentConfigInput{
DeploymentConfigName: &deploymentConfigName,
},
func(o *codedeploy.Options) {
o.Region = region
},
)
if err != nil {
return *GetDeploymentConfig.DeploymentConfigInfo, err
}
internal.Cache.Set(cacheKey, *GetDeploymentConfig.DeploymentConfigInfo, cache.DefaultExpiration)
return *GetDeploymentConfig.DeploymentConfigInfo, nil
}
func CachedCodeDeployGetDeployment(client AWSCodeDeployClientInterface, accountID string, region string, deploymentID string) (codeDeployTypes.DeploymentInfo, error) {
cacheKey := fmt.Sprintf("%s-codedeploy-GetDeployment-%s-%s", accountID, region, deploymentID)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.(codeDeployTypes.DeploymentInfo), nil
}
GetDeployment, err := client.GetDeployment(
context.TODO(),
&codedeploy.GetDeploymentInput{
DeploymentId: &deploymentID,
},
func(o *codedeploy.Options) {
o.Region = region
},
)
if err != nil {
return *GetDeployment.DeploymentInfo, err
}
internal.Cache.Set(cacheKey, *GetDeployment.DeploymentInfo, cache.DefaultExpiration)
return *GetDeployment.DeploymentInfo, nil
}
+85
View File
@@ -0,0 +1,85 @@
package sdk
import (
"context"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/codedeploy"
codedeployTypes "github.com/aws/aws-sdk-go-v2/service/codedeploy/types"
)
type MockedCodedeployClient struct {
}
func (m *MockedCodedeployClient) ListApplications(ctx context.Context, input *codedeploy.ListApplicationsInput, options ...func(*codedeploy.Options)) (*codedeploy.ListApplicationsOutput, error) {
return &codedeploy.ListApplicationsOutput{
Applications: []string{
"app1",
"app2",
},
}, nil
}
func (m *MockedCodedeployClient) ListDeployments(ctx context.Context, input *codedeploy.ListDeploymentsInput, options ...func(*codedeploy.Options)) (*codedeploy.ListDeploymentsOutput, error) {
return &codedeploy.ListDeploymentsOutput{
Deployments: []string{
"deployment1",
"deployment2",
},
}, nil
}
func (m *MockedCodedeployClient) ListDeploymentConfigs(ctx context.Context, input *codedeploy.ListDeploymentConfigsInput, options ...func(*codedeploy.Options)) (*codedeploy.ListDeploymentConfigsOutput, error) {
return &codedeploy.ListDeploymentConfigsOutput{
DeploymentConfigsList: []string{
"deploymentConfig1",
"deploymentConfig2",
},
}, nil
}
func (m *MockedCodedeployClient) GetApplication(ctx context.Context, input *codedeploy.GetApplicationInput, options ...func(*codedeploy.Options)) (*codedeploy.GetApplicationOutput, error) {
return &codedeploy.GetApplicationOutput{
Application: &codedeployTypes.ApplicationInfo{
ApplicationName: aws.String("application1"),
ApplicationId: aws.String("application1"),
CreateTime: aws.Time(time.Now()),
GitHubAccountName: aws.String("github"),
LinkedToGitHub: true,
},
}, nil
}
func (m *MockedCodedeployClient) GetDeployment(ctx context.Context, input *codedeploy.GetDeploymentInput, options ...func(*codedeploy.Options)) (*codedeploy.GetDeploymentOutput, error) {
return &codedeploy.GetDeploymentOutput{
DeploymentInfo: &codedeployTypes.DeploymentInfo{
ApplicationName: aws.String("application1"),
CreateTime: aws.Time(time.Now()),
DeploymentId: aws.String("deployment1"),
DeploymentOverview: &codedeployTypes.DeploymentOverview{
Failed: 0,
InProgress: 0,
Pending: 0,
Ready: 0,
Skipped: 0,
Succeeded: 0,
},
DeploymentConfigName: aws.String("deploymentConfig1"),
Status: codedeployTypes.DeploymentStatusSucceeded,
},
}, nil
}
func (m *MockedCodedeployClient) GetDeploymentConfig(ctx context.Context, input *codedeploy.GetDeploymentConfigInput, options ...func(*codedeploy.Options)) (*codedeploy.GetDeploymentConfigOutput, error) {
return &codedeploy.GetDeploymentConfigOutput{
DeploymentConfigInfo: &codedeployTypes.DeploymentConfigInfo{
DeploymentConfigName: aws.String("deploymentConfig1"),
ComputePlatform: codedeployTypes.ComputePlatformServer,
MinimumHealthyHosts: &codedeployTypes.MinimumHealthyHosts{
Type: codedeployTypes.MinimumHealthyHostsTypeFleetPercent,
Value: 100,
},
},
}, nil
}
+56
View File
@@ -0,0 +1,56 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/datapipeline"
dataPipelineTypes "github.com/aws/aws-sdk-go-v2/service/datapipeline/types"
"github.com/patrickmn/go-cache"
)
type AWSDataPipelineClientInterface interface {
ListPipelines(ctx context.Context, input *datapipeline.ListPipelinesInput, opts ...func(*datapipeline.Options)) (*datapipeline.ListPipelinesOutput, error)
}
func init() {
gob.Register([]dataPipelineTypes.PipelineIdName{})
}
func CachedDataPipelineListPipelines(client AWSDataPipelineClientInterface, accountID string, region string) ([]dataPipelineTypes.PipelineIdName, error) {
var PaginationControl *string
var pipelines []dataPipelineTypes.PipelineIdName
cacheKey := fmt.Sprintf("%s-datapipeline-ListPipelines-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]dataPipelineTypes.PipelineIdName), nil
}
for {
ListPipelines, err := client.ListPipelines(
context.TODO(),
&datapipeline.ListPipelinesInput{
Marker: PaginationControl,
},
func(o *datapipeline.Options) {
o.Region = region
},
)
if err != nil {
return pipelines, err
}
pipelines = append(pipelines, ListPipelines.PipelineIdList...)
//pagination
if ListPipelines.Marker == nil {
break
}
PaginationControl = ListPipelines.Marker
}
internal.Cache.Set(cacheKey, pipelines, cache.DefaultExpiration)
return pipelines, nil
}
+27
View File
@@ -0,0 +1,27 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/datapipeline"
dataPipelineTypes "github.com/aws/aws-sdk-go-v2/service/datapipeline/types"
)
type MockedDataPipelineClient struct {
}
func (m *MockedDataPipelineClient) ListPipelines(ctx context.Context, input *datapipeline.ListPipelinesInput, options ...func(*datapipeline.Options)) (*datapipeline.ListPipelinesOutput, error) {
return &datapipeline.ListPipelinesOutput{
PipelineIdList: []dataPipelineTypes.PipelineIdName{
{
Id: aws.String("pipeline1"),
Name: aws.String("pipeline1"),
},
{
Id: aws.String("pipeline2"),
Name: aws.String("pipeline2"),
},
},
}, nil
}
+1 -1
View File
@@ -19,7 +19,7 @@ type DocDBClientInterface interface {
DescribeDBInstances(context.Context, *docdb.DescribeDBInstancesInput, ...func(*docdb.Options)) (*docdb.DescribeDBInstancesOutput, error)
}
func RegisterDocDBTypes() {
func init() {
gob.Register([]docdbTypes.GlobalCluster{})
gob.Register([]docdbTypes.DBCluster{})
//gob.Register([]docdbTypes.DBInstance{})
+25
View File
@@ -0,0 +1,25 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/docdb"
docdbTypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
)
type MockedAWSDocsDBClient struct {
}
func (m *MockedAWSDocsDBClient) DescribeDBClusters(ctx context.Context, input *docdb.DescribeDBClustersInput, options ...func(*docdb.Options)) (*docdb.DescribeDBClustersOutput, error) {
return &docdb.DescribeDBClustersOutput{
DBClusters: []docdbTypes.DBCluster{
{
DBClusterIdentifier: aws.String("cluster1"),
},
{
DBClusterIdentifier: aws.String("cluster2"),
},
},
}, nil
}
+1 -1
View File
@@ -16,7 +16,7 @@ type DynamoDBClientInterface interface {
DescribeTable(context.Context, *dynamodb.DescribeTableInput, ...func(*dynamodb.Options)) (*dynamodb.DescribeTableOutput, error)
}
func RegisterDynamoDBTypes() {
func init() {
gob.Register([]string{})
gob.Register(dynamoDBTypes.TableDescription{})
}
+23
View File
@@ -0,0 +1,23 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
dynamodbTypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)
type MockedAWSDynamoDBClient struct {
}
func (m *MockedAWSDynamoDBClient) ListTables() ([]string, error) {
return []string{"table1", "table2"}, nil
}
func (m *MockedAWSDynamoDBClient) DescribeTable(ctx context.Context, input *dynamodb.DescribeTableInput, options ...func(*dynamodb.Options)) (*dynamodb.DescribeTableOutput, error) {
return &dynamodb.DescribeTableOutput{
Table: &dynamodbTypes.TableDescription{
TableName: input.TableName,
},
}, nil
}
+151 -3
View File
@@ -11,15 +11,24 @@ import (
"github.com/patrickmn/go-cache"
)
type EC2ClientInterface interface {
type AWSEC2ClientInterface interface {
DescribeInstances(context.Context, *ec2.DescribeInstancesInput, ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
DescribeNetworkInterfaces(context.Context, *ec2.DescribeNetworkInterfacesInput, ...func(*ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error)
DescribeSnapshots(context.Context, *ec2.DescribeSnapshotsInput, ...func(*ec2.Options)) (*ec2.DescribeSnapshotsOutput, error)
DescribeVolumes(context.Context, *ec2.DescribeVolumesInput, ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error)
DescribeImages(context.Context, *ec2.DescribeImagesInput, ...func(*ec2.Options)) (*ec2.DescribeImagesOutput, error)
}
func RegisterEC2Types() {
func init() {
gob.Register([]ec2Types.Instance{})
gob.Register([]ec2Types.NetworkInterface{})
gob.Register([]ec2Types.Snapshot{})
gob.Register([]ec2Types.Volume{})
gob.Register([]ec2Types.Image{})
}
func CachedEC2DescribeInstances(client EC2ClientInterface, accountID string, region string) ([]ec2Types.Instance, error) {
func CachedEC2DescribeInstances(client AWSEC2ClientInterface, accountID string, region string) ([]ec2Types.Instance, error) {
var PaginationControl *string
var instances []ec2Types.Instance
cacheKey := fmt.Sprintf("%s-ec2-DescribeInstances-%s", accountID, region)
@@ -56,3 +65,142 @@ func CachedEC2DescribeInstances(client EC2ClientInterface, accountID string, reg
internal.Cache.Set(cacheKey, instances, cache.DefaultExpiration)
return instances, nil
}
func CachedEC2DescribeNetworkInterfaces(client AWSEC2ClientInterface, accountID string, region string) ([]ec2Types.NetworkInterface, error) {
var PaginationControl *string
var NetworkInterfaces []ec2Types.NetworkInterface
cacheKey := fmt.Sprintf("%s-ec2-DescribeNetworkInterfaces-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]ec2Types.NetworkInterface), nil
}
for {
DescribeNetworkInterfaces, err := client.DescribeNetworkInterfaces(
context.TODO(),
&(ec2.DescribeNetworkInterfacesInput{
NextToken: PaginationControl,
}),
func(o *ec2.Options) {
o.Region = region
},
)
if err != nil {
return NetworkInterfaces, err
}
for _, networkInterface := range DescribeNetworkInterfaces.NetworkInterfaces {
NetworkInterfaces = append(NetworkInterfaces, networkInterface)
}
if DescribeNetworkInterfaces.NextToken == nil {
break
}
PaginationControl = DescribeNetworkInterfaces.NextToken
}
internal.Cache.Set(cacheKey, NetworkInterfaces, cache.DefaultExpiration)
return NetworkInterfaces, nil
}
func CachedEC2DescribeSnapshots(client AWSEC2ClientInterface, accountID string, region string) ([]ec2Types.Snapshot, error) {
var PaginationControl *string
var Snapshots []ec2Types.Snapshot
cacheKey := fmt.Sprintf("%s-ec2-DescribeSnapshots-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]ec2Types.Snapshot), nil
}
for {
DescribeSnapshots, err := client.DescribeSnapshots(
context.TODO(),
&(ec2.DescribeSnapshotsInput{
NextToken: PaginationControl,
OwnerIds: []string{accountID},
}),
func(o *ec2.Options) {
o.Region = region
},
)
if err != nil {
return Snapshots, err
}
for _, snapshot := range DescribeSnapshots.Snapshots {
Snapshots = append(Snapshots, snapshot)
}
if DescribeSnapshots.NextToken == nil {
break
}
PaginationControl = DescribeSnapshots.NextToken
}
internal.Cache.Set(cacheKey, Snapshots, cache.DefaultExpiration)
return Snapshots, nil
}
func CachedEC2DescribeVolumes(client AWSEC2ClientInterface, accountID string, region string) ([]ec2Types.Volume, error) {
var PaginationControl *string
var Volumes []ec2Types.Volume
cacheKey := fmt.Sprintf("%s-ec2-DescribeVolumes-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]ec2Types.Volume), nil
}
for {
DescribeVolumes, err := client.DescribeVolumes(
context.TODO(),
&(ec2.DescribeVolumesInput{
NextToken: PaginationControl,
}),
func(o *ec2.Options) {
o.Region = region
},
)
if err != nil {
return Volumes, err
}
for _, volume := range DescribeVolumes.Volumes {
Volumes = append(Volumes, volume)
}
if DescribeVolumes.NextToken == nil {
break
}
PaginationControl = DescribeVolumes.NextToken
}
internal.Cache.Set(cacheKey, Volumes, cache.DefaultExpiration)
return Volumes, nil
}
func CachedEC2DescribeImages(client AWSEC2ClientInterface, accountID string, region string) ([]ec2Types.Image, error) {
var PaginationControl *string
var Images []ec2Types.Image
cacheKey := fmt.Sprintf("%s-ec2-DescribeImages-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]ec2Types.Image), nil
}
for {
DescribeImages, err := client.DescribeImages(
context.TODO(),
&(ec2.DescribeImagesInput{
Owners: []string{accountID},
NextToken: PaginationControl,
}),
func(o *ec2.Options) {
o.Region = region
},
)
if err != nil {
return Images, err
}
for _, image := range DescribeImages.Images {
Images = append(Images, image)
}
if DescribeImages.NextToken == nil {
break
}
PaginationControl = DescribeImages.NextToken
}
internal.Cache.Set(cacheKey, Images, cache.DefaultExpiration)
return Images, nil
}
+374
View File
@@ -0,0 +1,374 @@
package sdk
// create test for the Cached EC2 functions. Create a mocked client and mocked functions for each of the methods used in the ec2.go file
import (
"context"
"encoding/json"
"log"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
)
const DESCRIBE_NETWORK_INTEFACES_TEST_FILE = "./test-data/describe-network-interfaces.json"
const DESCRIBE_INSTANCES_TEST_FILE = "./test-data/ec2-describeInstances.json"
const DESCRIBE_SNAPSHOTS_TEST_FILE = "./test-data/ec2-describeSnapshots.json"
const DESCRIBE_VOLUMES_TEST_FILE = "./test-data/ec2-describeVolumes.json"
const DESCRIBE_IMAGES_TEST_FILE = "./test-data/ec2-describeImages.json"
type MockedEC2Client2 struct {
describeNetworkInterfaces DescribeNetworkInterfaces
describeInstances DescribeInstances
describeSnapshots DescribeSnapshots
describeVolumes DescribeVolumes
describeImages DescribeImages
}
type DescribeNetworkInterfaces struct {
NetworkInterfaces []struct {
Status string `json:"Status"`
MacAddress string `json:"MacAddress"`
SourceDestCheck bool `json:"SourceDestCheck"`
VpcID string `json:"VpcId"`
Description string `json:"Description"`
Association struct {
PublicIP string `json:"PublicIp"`
AssociationID string `json:"AssociationId"`
PublicDNSName string `json:"PublicDnsName"`
IPOwnerID string `json:"IpOwnerId"`
} `json:"Association"`
NetworkInterfaceID string `json:"NetworkInterfaceId"`
PrivateIPAddresses []struct {
PrivateDNSName string `json:"PrivateDnsName"`
Association struct {
PublicIP string `json:"PublicIp"`
AssociationID string `json:"AssociationId"`
PublicDNSName string `json:"PublicDnsName"`
IPOwnerID string `json:"IpOwnerId"`
} `json:"Association"`
Primary bool `json:"Primary"`
PrivateIPAddress string `json:"PrivateIpAddress"`
} `json:"PrivateIpAddresses"`
RequesterManaged bool `json:"RequesterManaged"`
Ipv6Addresses []interface{} `json:"Ipv6Addresses"`
PrivateDNSName string `json:"PrivateDnsName,omitempty"`
AvailabilityZone string `json:"AvailabilityZone"`
Attachment struct {
Status string `json:"Status"`
DeviceIndex int `json:"DeviceIndex"`
AttachTime time.Time `json:"AttachTime"`
InstanceID string `json:"InstanceId"`
DeleteOnTermination bool `json:"DeleteOnTermination"`
AttachmentID string `json:"AttachmentId"`
InstanceOwnerID string `json:"InstanceOwnerId"`
} `json:"Attachment"`
Groups []struct {
GroupName string `json:"GroupName"`
GroupID string `json:"GroupId"`
} `json:"Groups"`
SubnetID string `json:"SubnetId"`
OwnerID string `json:"OwnerId"`
TagSet []interface{} `json:"TagSet"`
PrivateIPAddress string `json:"PrivateIpAddress"`
} `json:"NetworkInterfaces"`
}
type DescribeInstances struct {
Reservations []struct {
Instances []struct {
AmiLaunchIndex int `json:"AmiLaunchIndex"`
ImageID string `json:"ImageId"`
InstanceID string `json:"InstanceId"`
InstanceType string `json:"InstanceType"`
KernelID string `json:"KernelId"`
KeyName string `json:"KeyName"`
LaunchTime string `json:"LaunchTime"`
Monitoring struct {
State string `json:"State"`
} `json:"Monitoring"`
Placement struct {
AvailabilityZone string `json:"AvailabilityZone"`
GroupName string `json:"GroupName"`
Tenancy string `json:"Tenancy"`
} `json:"Placement"`
Platform string `json:"Platform"`
PrivateDNS string `json:"PrivateDnsName"`
PrivateIP string `json:"PrivateIpAddress"`
PublicDNS string `json:"PublicDnsName"`
PublicIP string `json:"PublicIpAddress"`
State string `json:"State"`
SubnetID string `json:"SubnetId"`
VpcID string `json:"VpcId"`
Architecture string `json:"Architecture"`
BlockDeviceMappings []struct {
DeviceName string `json:"DeviceName"`
Ebs struct {
AttachTime time.Time `json:"AttachTime"`
DeleteOnTermination bool `json:"DeleteOnTermination"`
Status string `json:"Status"`
VolumeID string `json:"VolumeId"`
} `json:"Ebs"`
} `json:"BlockDeviceMappings"`
ClientToken string `json:"ClientToken"`
EbsOptimized bool `json:"EbsOptimized"`
Hypervisor string `json:"Hypervisor"`
IamInstanceProfile struct {
Arn string `json:"Arn"`
ID string `json:"Id"`
} `json:"IamInstanceProfile"`
NetworkInterfaces []struct {
Attachment struct {
AttachTime time.Time `json:"AttachTime"`
AttachmentID string `json:"AttachmentId"`
DeleteOnTermination bool `json:"DeleteOnTermination"`
DeviceIndex int `json:"DeviceIndex"`
Status string `json:"Status"`
} `json:"Attachment"`
Description string `json:"Description"`
Groups []struct {
GroupName string `json:"GroupName"`
GroupID string `json:"GroupId"`
} `json:"Groups"`
MacAddress string `json:"MacAddress"`
NetworkInterfaceID string `json:"NetworkInterfaceId"`
OwnerID string `json:"OwnerId"`
PrivateDNSName string `json:"PrivateDnsName"`
PrivateIPAddress string `json:"PrivateIpAddress"`
PrivateIPAddresses []struct {
Association struct {
IPOwnerID string `json:"IpOwnerId"`
PublicDNSName string `json:"PublicDnsName"`
PublicIP string `json:"PublicIp"`
} `json:"Association"`
Primary bool `json:"Primary"`
PrivateDNSName string `json:"PrivateDnsName"`
PrivateIPAddress string `json:"PrivateIpAddress"`
} `json:"PrivateIpAddresses"`
SourceDestCheck bool `json:"SourceDestCheck"`
Status string `json:"Status"`
SubnetID string `json:"SubnetId"`
VpcID string `json:"VpcId"`
} `json:"NetworkInterfaces"`
RootDeviceName string `json:"RootDeviceName"`
RootDeviceType string `json:"RootDeviceType"`
SecurityGroups []struct {
GroupName string `json:"GroupName"`
GroupID string `json:"GroupId"`
} `json:"SecurityGroups"`
SourceDestCheck bool `json:"SourceDestCheck"`
StateReason struct {
Code string `json:"Code"`
Message string `json:"Message"`
} `json:"StateReason"`
Tags []struct {
Key string `json:"Key"`
Value string `json:"Value"`
} `json:"Tags"`
VirtualizationType string `json:"VirtualizationType"`
} `json:"Instances"`
OwnerID string `json:"OwnerId"`
RequesterID string `json:"RequesterId"`
ReservationID string `json:"ReservationId"`
} `json:"Reservations"`
}
type DescribeImages struct {
Images []struct {
Architecture string `json:"Architecture"`
BlockDeviceMappings []struct {
DeviceName string `json:"DeviceName"`
Ebs struct {
DeleteOnTermination bool `json:"DeleteOnTermination"`
SnapshotID string `json:"SnapshotId"`
VolumeSize int `json:"VolumeSize"`
VolumeType string `json:"VolumeType"`
} `json:"Ebs"`
} `json:"BlockDeviceMappings"`
CreationDate string `json:"CreationDate"`
Description string `json:"Description"`
EnaSupport bool `json:"EnaSupport"`
Hypervisor string `json:"Hypervisor"`
ImageID string `json:"ImageId"`
ImageLocation string `json:"ImageLocation"`
ImageType string `json:"ImageType"`
KernelID string `json:"KernelId"`
Name string `json:"Name"`
OwnerAlias string `json:"OwnerAlias"`
OwnerID string `json:"OwnerId"`
Platform string `json:"Platform"`
ProductCodes []struct {
ProductCodeID string `json:"ProductCodeId"`
ProductCodeType string `json:"ProductCodeType"`
} `json:"ProductCodes"`
Public bool `json:"Public"`
RamdiskID string `json:"RamdiskId"`
RootDeviceName string `json:"RootDeviceName"`
RootDeviceType string `json:"RootDeviceType"`
SriovNetSupport string `json:"SriovNetSupport"`
State string `json:"State"`
StateReason struct {
Code string `json:"Code"`
Message string `json:"Message"`
} `json:"StateReason"`
Tags []struct {
Key string `json:"Key"`
Value string `json:"Value"`
} `json:"Tags"`
VirtualizationType string `json:"VirtualizationType"`
} `json:"Images"`
NextToken string `json:"NextToken"`
}
type DescribeSnapshots struct {
Snapshots []struct {
DataEncryptionKeyID string `json:"DataEncryptionKeyId"`
Description string `json:"Description"`
Encrypted bool `json:"Encrypted"`
KMSKeyID string `json:"KmsKeyId"`
OwnerAlias string `json:"OwnerAlias"`
OwnerID string `json:"OwnerId"`
Progress string `json:"Progress"`
SnapshotID string `json:"SnapshotId"`
StartTime time.Time `json:"StartTime"`
State string `json:"State"`
StateMessage string `json:"StateMessage"`
TagSet []struct {
Key string `json:"Key"`
Value string `json:"Value"`
} `json:"Tags"`
VolumeID string `json:"VolumeId"`
VolumeSize int `json:"VolumeSize"`
} `json:"Snapshots"`
}
type DescribeVolumes struct {
Volumes []struct {
Attachments []struct {
AttachTime time.Time `json:"AttachTime"`
DeleteOnTermination bool `json:"DeleteOnTermination"`
Device string `json:"Device"`
InstanceID string `json:"InstanceId"`
State string `json:"State"`
VolumeID string `json:"VolumeId"`
} `json:"Attachments"`
AvailabilityZone string `json:"AvailabilityZone"`
CreateTime string `json:"CreateTime"`
Encrypted bool `json:"Encrypted"`
KMSKeyID string `json:"KmsKeyId"`
Size int `json:"Size"`
SnapshotID string `json:"SnapshotId"`
State string `json:"State"`
VolumeID string `json:"VolumeId"`
} `json:"Volumes"`
}
func (c *MockedEC2Client2) DescribeNetworkInterfaces(ctx context.Context, input *ec2.DescribeNetworkInterfacesInput, f ...func(o *ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) {
var nics []ec2types.NetworkInterface
err := json.Unmarshal(readTestFile(DESCRIBE_NETWORK_INTEFACES_TEST_FILE), &c.describeNetworkInterfaces)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_NETWORK_INTEFACES_TEST_FILE)
}
for _, mockednic := range c.describeNetworkInterfaces.NetworkInterfaces {
nics = append(nics, ec2types.NetworkInterface{
Association: &ec2types.NetworkInterfaceAssociation{
PublicIp: aws.String(mockednic.Association.PublicIP),
},
NetworkInterfaceId: aws.String(mockednic.NetworkInterfaceID),
PrivateIpAddress: aws.String(mockednic.PrivateIPAddress),
VpcId: aws.String(mockednic.VpcID),
Attachment: &ec2types.NetworkInterfaceAttachment{InstanceId: aws.String(mockednic.Attachment.InstanceID)},
Description: aws.String(mockednic.Description),
})
}
return &ec2.DescribeNetworkInterfacesOutput{NetworkInterfaces: nics}, nil
}
func (c *MockedEC2Client2) DescribeInstances(ctx context.Context, input *ec2.DescribeInstancesInput, f ...func(o *ec2.Options)) (*ec2.DescribeInstancesOutput, error) {
var instances []ec2types.Instance
err := json.Unmarshal(readTestFile(DESCRIBE_INSTANCES_TEST_FILE), &c.describeInstances)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_INSTANCES_TEST_FILE)
}
for _, mockedReservation := range c.describeInstances.Reservations {
for _, mockedInstance := range mockedReservation.Instances {
for _, inputInstanceID := range input.InstanceIds {
if mockedInstance.InstanceID == inputInstanceID {
instances = append(instances, ec2types.Instance{
InstanceId: aws.String(mockedInstance.InstanceID),
InstanceType: ec2types.InstanceType(mockedInstance.InstanceType),
ImageId: aws.String(mockedInstance.ImageID),
})
}
}
}
}
return &ec2.DescribeInstancesOutput{Reservations: []ec2types.Reservation{
{Instances: instances},
}}, nil
}
func (c *MockedEC2Client2) DescribeVolumes(ctx context.Context, input *ec2.DescribeVolumesInput, f ...func(o *ec2.Options)) (*ec2.DescribeVolumesOutput, error) {
var volumes []ec2types.Volume
err := json.Unmarshal(readTestFile(DESCRIBE_VOLUMES_TEST_FILE), &c.describeVolumes)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_VOLUMES_TEST_FILE)
}
for _, mockedVolume := range c.describeVolumes.Volumes {
for _, inputVolumeID := range input.VolumeIds {
if mockedVolume.VolumeID == inputVolumeID {
volumes = append(volumes, ec2types.Volume{
VolumeId: aws.String(mockedVolume.VolumeID),
Size: aws.Int32(int32(mockedVolume.Size)),
Attachments: []ec2types.VolumeAttachment{
{
InstanceId: aws.String(mockedVolume.Attachments[0].InstanceID),
},
},
})
}
}
}
return &ec2.DescribeVolumesOutput{Volumes: volumes}, nil
}
func (c *MockedEC2Client2) DescribeSnapshots(ctx context.Context, input *ec2.DescribeSnapshotsInput, f ...func(o *ec2.Options)) (*ec2.DescribeSnapshotsOutput, error) {
var snapshots []ec2types.Snapshot
err := json.Unmarshal(readTestFile(DESCRIBE_SNAPSHOTS_TEST_FILE), &c.describeSnapshots)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_SNAPSHOTS_TEST_FILE)
}
for _, mockedSnapshot := range c.describeSnapshots.Snapshots {
for _, inputSnapshotID := range input.SnapshotIds {
if mockedSnapshot.SnapshotID == inputSnapshotID {
snapshots = append(snapshots, ec2types.Snapshot{
SnapshotId: aws.String(mockedSnapshot.SnapshotID),
VolumeId: aws.String(mockedSnapshot.VolumeID),
State: ec2types.SnapshotState(mockedSnapshot.State),
})
}
}
}
return &ec2.DescribeSnapshotsOutput{Snapshots: snapshots}, nil
}
func (c *MockedEC2Client2) DescribeImages(ctx context.Context, input *ec2.DescribeImagesInput, f ...func(o *ec2.Options)) (*ec2.DescribeImagesOutput, error) {
var images []ec2types.Image
err := json.Unmarshal(readTestFile(DESCRIBE_IMAGES_TEST_FILE), &c.describeImages)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_IMAGES_TEST_FILE)
}
for _, mockedImage := range c.describeImages.Images {
for _, inputImageID := range input.ImageIds {
if mockedImage.ImageID == inputImageID {
images = append(images, ec2types.Image{
ImageId: aws.String(mockedImage.ImageID),
Name: aws.String(mockedImage.Name),
})
}
}
}
return &ec2.DescribeImagesOutput{Images: images}, nil
}
+2 -1
View File
@@ -19,9 +19,10 @@ type AWSECRClientInterface interface {
GetRepositoryPolicy(ctx context.Context, params *ecr.GetRepositoryPolicyInput, optFns ...func(*ecr.Options)) (*ecr.GetRepositoryPolicyOutput, error)
}
func RegisterECRTypes() {
func init() {
gob.Register([]ecrTypes.Repository{})
gob.Register([]ecrTypes.ImageDetail{})
gob.Register(ecrTypes.Repository{})
}
+73
View File
@@ -0,0 +1,73 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecr"
ecrTypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
)
type MockedECRClient struct {
}
func (m *MockedECRClient) DescribeRepositories(ctx context.Context, input *ecr.DescribeRepositoriesInput, options ...func(*ecr.Options)) (*ecr.DescribeRepositoriesOutput, error) {
return &ecr.DescribeRepositoriesOutput{
Repositories: []ecrTypes.Repository{
{
RepositoryName: aws.String("repo1"),
},
{
RepositoryName: aws.String("repo2"),
},
},
}, nil
}
func (m *MockedECRClient) DescribeImages(ctx context.Context, input *ecr.DescribeImagesInput, options ...func(*ecr.Options)) (*ecr.DescribeImagesOutput, error) {
return &ecr.DescribeImagesOutput{
ImageDetails: []ecrTypes.ImageDetail{
{
ImageTags: []string{
"tag1",
"tag2",
},
},
{
ImageTags: []string{
"tag3", "tag4", "tag5", "tag6"},
},
},
}, nil
}
func (m *MockedECRClient) GetRepositoryPolicy(ctx context.Context, input *ecr.GetRepositoryPolicyInput, options ...func(*ecr.Options)) (*ecr.GetRepositoryPolicyOutput, error) {
return &ecr.GetRepositoryPolicyOutput{
PolicyText: aws.String(`{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "AllowPushPull",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::123456789012:root",
"arn:aws:iam::123456789012:user/MyUser"
]
},
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
]
}
]
}`),
}, nil
}
+215
View File
@@ -0,0 +1,215 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"strings"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/ecs"
ecsTypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/patrickmn/go-cache"
)
type AWSECSClientInterface interface {
ListClusters(ctx context.Context, params *ecs.ListClustersInput, optFns ...func(*ecs.Options)) (*ecs.ListClustersOutput, error)
ListTasks(ctx context.Context, params *ecs.ListTasksInput, optFns ...func(*ecs.Options)) (*ecs.ListTasksOutput, error)
ListServices(ctx context.Context, params *ecs.ListServicesInput, optFns ...func(*ecs.Options)) (*ecs.ListServicesOutput, error)
DescribeTasks(ctx context.Context, params *ecs.DescribeTasksInput, optFns ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error)
DescribeTaskDefinition(ctx context.Context, params *ecs.DescribeTaskDefinitionInput, optFns ...func(*ecs.Options)) (*ecs.DescribeTaskDefinitionOutput, error)
}
func init() {
gob.Register([]string{})
gob.Register(ecsTypes.Task{})
gob.Register(ecsTypes.TaskDefinition{})
}
func CachedECSListClusters(ECSClient AWSECSClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var clusters []string
cacheKey := fmt.Sprintf("%s-ecs-ListClusters-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached ECS clusters data")
return cached.([]string), nil
}
for {
ListClusters, err := ECSClient.ListClusters(
context.TODO(),
&ecs.ListClustersInput{
NextToken: PaginationControl,
},
func(o *ecs.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
clusters = append(clusters, ListClusters.ClusterArns...)
// Pagination control.
if ListClusters.NextToken != nil {
PaginationControl = ListClusters.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, clusters, cache.DefaultExpiration)
return clusters, nil
}
func CachedECSListTasks(ECSClient AWSECSClientInterface, accountID string, region string, cluster string) ([]string, error) {
var PaginationControl *string
var tasks []string
//grab cluster name from AWS ARN
clusterName := cluster[strings.LastIndex(cluster, "/")+1:]
cacheKey := fmt.Sprintf("%s-ecs-ListTasks-%s-%s", accountID, region, clusterName)
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached ECS tasks data")
return cached.([]string), nil
}
for {
ListTasks, err := ECSClient.ListTasks(
context.TODO(),
&ecs.ListTasksInput{
Cluster: &cluster,
NextToken: PaginationControl,
},
func(o *ecs.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
tasks = append(tasks, ListTasks.TaskArns...)
// Pagination control.
if ListTasks.NextToken != nil {
PaginationControl = ListTasks.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, tasks, cache.DefaultExpiration)
return tasks, nil
}
func CachedECSDescribeTasks(ECSClient AWSECSClientInterface, accountID string, region string, cluster string, tasks []string) ([]ecsTypes.Task, error) {
var taskDetails []ecsTypes.Task
cacheKey := fmt.Sprintf("%s-ecs-DescribeTasks-%s-%s", accountID, region, cluster)
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached ECS task details data")
return cached.([]ecsTypes.Task), nil
}
DescribeTasks, err := ECSClient.DescribeTasks(
context.TODO(),
&ecs.DescribeTasksInput{
Cluster: &cluster,
Tasks: tasks,
},
func(o *ecs.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
return []ecsTypes.Task{}, err
}
taskDetails = append(taskDetails, DescribeTasks.Tasks...)
internal.Cache.Set(cacheKey, taskDetails, cache.DefaultExpiration)
return taskDetails, nil
}
func CachedECSDescribeTaskDefinition(ECSClient AWSECSClientInterface, accountID string, region string, taskDefinition string) (ecsTypes.TaskDefinition, error) {
var taskDefinitionDetails ecsTypes.TaskDefinition
cacheKey := fmt.Sprintf("%s-ecs-DescribeTaskDefinition-%s-%s", accountID, region, taskDefinition)
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached ECS task definition details data")
return cached.(ecsTypes.TaskDefinition), nil
}
DescribeTaskDefinition, err := ECSClient.DescribeTaskDefinition(
context.TODO(),
&ecs.DescribeTaskDefinitionInput{
TaskDefinition: &taskDefinition,
},
func(o *ecs.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
return ecsTypes.TaskDefinition{}, err
}
taskDefinitionDetails = *DescribeTaskDefinition.TaskDefinition
internal.Cache.Set(cacheKey, taskDefinitionDetails, cache.DefaultExpiration)
return taskDefinitionDetails, nil
}
func CachedECSListServices(ECSClient AWSECSClientInterface, accountID string, region string, cluster string) ([]string, error) {
var PaginationControl *string
var services []string
//grab cluster name from AWS ARN
clusterName := cluster[strings.LastIndex(cluster, "/")+1:]
cacheKey := fmt.Sprintf("%s-ecs-ListServices-%s-%s", accountID, region, clusterName)
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached ECS services data")
return cached.([]string), nil
}
for {
ListServices, err := ECSClient.ListServices(
context.TODO(),
&ecs.ListServicesInput{
Cluster: &cluster,
NextToken: PaginationControl,
},
func(o *ecs.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
services = append(services, ListServices.ServiceArns...)
// Pagination control.
if ListServices.NextToken != nil {
PaginationControl = ListServices.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, services, cache.DefaultExpiration)
return services, nil
}
+156
View File
@@ -0,0 +1,156 @@
package sdk
import (
"context"
"encoding/json"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecs"
ecsTypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
)
const DESCRIBE_TASKS_TEST_FILE = "./test-data/describe-tasks.json"
type MockedECSClient struct {
describeTasks DescribeTasks
}
type ListTasks struct {
TaskArns []string `json:"taskArns"`
}
type DescribeTasks struct {
Tasks []struct {
Attachments []struct {
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
Details []struct {
Name string `json:"name"`
Value string `json:"value"`
} `json:"details"`
} `json:"attachments"`
Attributes []struct {
Name string `json:"name"`
Value string `json:"value"`
} `json:"attributes"`
AvailabilityZone string `json:"availabilityZone"`
ClusterArn string `json:"clusterArn"`
Connectivity string `json:"connectivity"`
ConnectivityAt string `json:"connectivityAt"`
Containers []struct {
ContainerArn string `json:"containerArn"`
TaskArn string `json:"taskArn"`
Name string `json:"name"`
Image string `json:"image"`
RuntimeID string `json:"runtimeId"`
LastStatus string `json:"lastStatus"`
NetworkBindings []interface{} `json:"networkBindings"`
NetworkInterfaces []struct {
AttachmentID string `json:"attachmentId"`
PrivateIpv4Address string `json:"privateIpv4Address"`
} `json:"networkInterfaces"`
HealthStatus string `json:"healthStatus"`
CPU string `json:"cpu"`
Memory string `json:"memory"`
} `json:"containers"`
CPU string `json:"cpu"`
CreatedAt string `json:"createdAt"`
DesiredStatus string `json:"desiredStatus"`
EnableExecuteCommand bool `json:"enableExecuteCommand"`
Group string `json:"group"`
HealthStatus string `json:"healthStatus"`
LastStatus string `json:"lastStatus"`
LaunchType string `json:"launchType"`
Memory string `json:"memory"`
Overrides struct {
ContainerOverrides []struct {
Name string `json:"name"`
} `json:"containerOverrides"`
InferenceAcceleratorOverrides []interface{} `json:"inferenceAcceleratorOverrides"`
} `json:"overrides"`
PlatformVersion string `json:"platformVersion"`
PlatformFamily string `json:"platformFamily"`
PullStartedAt string `json:"pullStartedAt"`
PullStoppedAt string `json:"pullStoppedAt"`
StartedAt string `json:"startedAt"`
StartedBy string `json:"startedBy"`
Tags []interface{} `json:"tags"`
TaskArn string `json:"taskArn"`
TaskDefinitionArn string `json:"taskDefinitionArn"`
Version int `json:"version"`
EphemeralStorage struct {
SizeInGiB int `json:"sizeInGiB"`
} `json:"ephemeralStorage"`
} `json:"tasks"`
Failures []interface{} `json:"failures"`
}
func (c *MockedECSClient) ListClusters(context.Context, *ecs.ListClustersInput, ...func(*ecs.Options)) (*ecs.ListClustersOutput, error) {
return &ecs.ListClustersOutput{ClusterArns: []string{
"arn:aws:ecs:us-east-1:123456789012:cluster/MyCluster",
"arn:aws:ecs:us-east-1:123456789012:cluster/MyCluster2",
"arn:aws:ecs:us-east-1:123456789012:cluster/MyCluster3",
}}, nil
}
func (c *MockedECSClient) ListTasks(ctx context.Context, input *ecs.ListTasksInput, f ...func(*ecs.Options)) (*ecs.ListTasksOutput, error) {
return &ecs.ListTasksOutput{TaskArns: []string{
"arn:aws:ecs:us-east-1:123456789012:task/MyCluster/74de0355a10a4f979ac495c14EXAMPLE",
"arn:aws:ecs:us-east-1:123456789012:task/MyCluster/d789e94343414c25b9f6bd59eEXAMPLE",
}}, nil
}
func (c *MockedECSClient) ListServices(ctx context.Context, input *ecs.ListServicesInput, f ...func(*ecs.Options)) (*ecs.ListServicesOutput, error) {
return &ecs.ListServicesOutput{ServiceArns: []string{
"arn:aws:ecs:us-east-1:123456789012:service/MyService",
"arn:aws:ecs:us-east-1:123456789012:service/MyService2",
"arn:aws:ecs:us-east-1:123456789012:service/MyService3",
}}, nil
}
func (c *MockedECSClient) DescribeTasks(ctx context.Context, input *ecs.DescribeTasksInput, f ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error) {
err := json.Unmarshal(readTestFile(DESCRIBE_TASKS_TEST_FILE), &c.describeTasks)
if err != nil {
log.Fatalf("can't unmarshall file %s", DESCRIBE_TASKS_TEST_FILE)
}
var tasks []ecsTypes.Task
for _, mockedTask := range c.describeTasks.Tasks {
if mockedTask.ClusterArn == aws.ToString(input.Cluster) {
for _, inputTask := range input.Tasks {
if mockedTask.TaskArn == inputTask {
var attachments []ecsTypes.Attachment
for _, a := range mockedTask.Attachments {
var deets []ecsTypes.KeyValuePair
for _, detail := range a.Details {
deets = append(deets, ecsTypes.KeyValuePair{
Name: aws.String(detail.Name),
Value: aws.String(detail.Value)})
}
attachments = append(attachments, ecsTypes.Attachment{
Type: aws.String(a.Type),
Details: deets,
Id: aws.String(a.ID),
Status: aws.String(a.Status),
})
}
tasks = append(tasks, ecsTypes.Task{
ClusterArn: aws.String(mockedTask.ClusterArn),
TaskDefinitionArn: aws.String(mockedTask.TaskDefinitionArn),
LaunchType: ecsTypes.LaunchType(*aws.String(mockedTask.LaunchType)),
TaskArn: aws.String(mockedTask.TaskArn),
Attachments: attachments,
})
}
}
}
}
return &ecs.DescribeTasksOutput{Tasks: tasks}, nil
}
func (c *MockedECSClient) DescribeTaskDefinition(ctx context.Context, input *ecs.DescribeTaskDefinitionInput, f ...func(o *ecs.Options)) (*ecs.DescribeTaskDefinitionOutput, error) {
testTaskDefinition := ecsTypes.TaskDefinition{}
testTaskDefinition.TaskRoleArn = aws.String("test123")
return &ecs.DescribeTaskDefinitionOutput{TaskDefinition: &testTaskDefinition}, nil
}
+1 -1
View File
@@ -20,7 +20,7 @@ type AWSEFSClientInterface interface {
DescribeFileSystemPolicy(ctx context.Context, params *efs.DescribeFileSystemPolicyInput, optFns ...func(*efs.Options)) (*efs.DescribeFileSystemPolicyOutput, error)
}
func RegisterEFSTypes() {
func init() {
gob.Register([]efsTypes.FileSystemDescription{})
gob.Register([]efsTypes.MountTargetDescription{})
gob.Register([]efsTypes.AccessPointDescription{})
+67
View File
@@ -0,0 +1,67 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/efs"
efsTypes "github.com/aws/aws-sdk-go-v2/service/efs/types"
)
type MockedEfsClient struct {
}
func (m *MockedEfsClient) DescribeFileSystems(ctx context.Context, input *efs.DescribeFileSystemsInput, options ...func(*efs.Options)) (*efs.DescribeFileSystemsOutput, error) {
return &efs.DescribeFileSystemsOutput{
FileSystems: []efsTypes.FileSystemDescription{
{
FileSystemId: aws.String("fs-12345678"),
},
{
FileSystemId: aws.String("fs-87654321"),
},
},
}, nil
}
func (m *MockedEfsClient) DescribeMountTargets(ctx context.Context, input *efs.DescribeMountTargetsInput, options ...func(*efs.Options)) (*efs.DescribeMountTargetsOutput, error) {
return &efs.DescribeMountTargetsOutput{
MountTargets: []efsTypes.MountTargetDescription{
{
MountTargetId: aws.String("fsmt-12345678"),
FileSystemId: aws.String("fs-12345678"),
IpAddress: aws.String("10.1.1.1"),
},
{
MountTargetId: aws.String("fsmt-87654321"),
FileSystemId: aws.String("fs-87654321"),
IpAddress: aws.String("10.2.2.2.2"),
},
},
}, nil
}
func (m *MockedEfsClient) DescribeAccessPoints(ctx context.Context, input *efs.DescribeAccessPointsInput, options ...func(*efs.Options)) (*efs.DescribeAccessPointsOutput, error) {
return &efs.DescribeAccessPointsOutput{
AccessPoints: []efsTypes.AccessPointDescription{
{
AccessPointId: aws.String("fsap-12345678"),
FileSystemId: aws.String("fs-12345678"),
Name: aws.String("fsap-12345678"),
PosixUser: &efsTypes.PosixUser{
Gid: aws.Int64(1000),
Uid: aws.Int64(1000),
},
},
{
AccessPointId: aws.String("fsap-87654321"),
FileSystemId: aws.String("fs-87654321"),
Name: aws.String("fsap-12345679"),
PosixUser: &efsTypes.PosixUser{
Gid: aws.Int64(1000),
Uid: aws.Int64(1000),
},
},
},
}, nil
}
+1 -1
View File
@@ -18,7 +18,7 @@ type EKSClientInterface interface {
ListNodegroups(context.Context, *eks.ListNodegroupsInput, ...func(*eks.Options)) (*eks.ListNodegroupsOutput, error)
}
func RegisterEKSTypes() {
func init() {
gob.Register([]string{})
gob.Register(eksTypes.Cluster{})
gob.Register(eksTypes.Nodegroup{})
+54
View File
@@ -0,0 +1,54 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/eks"
eksTypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
)
type MockedAWSEksClient struct {
}
func (m *MockedAWSEksClient) ListClusters(ctx context.Context, input *eks.ListClustersInput, options ...func(*eks.Options)) (*eks.ListClustersOutput, error) {
return &eks.ListClustersOutput{
Clusters: []string{
"cluster1",
"cluster2",
},
}, nil
}
func (m *MockedAWSEksClient) DescribeCluster(ctx context.Context, input *eks.DescribeClusterInput, options ...func(*eks.Options)) (*eks.DescribeClusterOutput, error) {
return &eks.DescribeClusterOutput{
Cluster: &eksTypes.Cluster{
Arn: aws.String("arn:aws:eks:us-east-1:123456789012:cluster/cluster1"),
Endpoint: aws.String("https://cluster1.us-east-1.eks.amazonaws.com"),
Name: aws.String("cluster1"),
PlatformVersion: aws.String("eks.1"),
Version: aws.String("1.18"),
RoleArn: aws.String("arn:aws:iam::123456789012:role/eks-role"),
},
}, nil
}
func (m *MockedAWSEksClient) ListNodegroups(ctx context.Context, input *eks.ListNodegroupsInput, options ...func(*eks.Options)) (*eks.ListNodegroupsOutput, error) {
return &eks.ListNodegroupsOutput{
Nodegroups: []string{
"nodegroup1",
"nodegroup2",
},
}, nil
}
func (m *MockedAWSEksClient) DescribeNodegroup(ctx context.Context, input *eks.DescribeNodegroupInput, options ...func(*eks.Options)) (*eks.DescribeNodegroupOutput, error) {
return &eks.DescribeNodegroupOutput{
Nodegroup: &eksTypes.Nodegroup{
ClusterName: aws.String("cluster1"),
NodeRole: aws.String("arn:aws:iam::123456789012:role/eks-role"),
NodegroupName: aws.String("nodegroup1"),
NodegroupArn: aws.String("arn:aws:eks:us-east-1:123456789012:nodegroup/cluster1/nodegroup1"),
},
}, nil
}
+57
View File
@@ -0,0 +1,57 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/elasticache"
elasticacheTypes "github.com/aws/aws-sdk-go-v2/service/elasticache/types"
"github.com/patrickmn/go-cache"
)
type AWSElastiCacheClientInterface interface {
DescribeCacheClusters(context.Context, *elasticache.DescribeCacheClustersInput, ...func(*elasticache.Options)) (*elasticache.DescribeCacheClustersOutput, error)
}
func init() {
gob.Register([]elasticacheTypes.CacheCluster{})
}
func CachedElastiCacheDescribeCacheClusters(client AWSElastiCacheClientInterface, accountID string, region string) ([]elasticacheTypes.CacheCluster, error) {
var PaginationControl *string
var clusters []elasticacheTypes.CacheCluster
cacheKey := fmt.Sprintf("%s-elasticache-DescribeCacheClusters-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]elasticacheTypes.CacheCluster), nil
}
for {
DescribeCacheClusters, err := client.DescribeCacheClusters(
context.TODO(),
&elasticache.DescribeCacheClustersInput{
Marker: PaginationControl,
},
func(o *elasticache.Options) {
o.Region = region
},
)
if err != nil {
return clusters, err
}
clusters = append(clusters, DescribeCacheClusters.CacheClusters...)
//pagination
if DescribeCacheClusters.Marker == nil {
break
}
PaginationControl = DescribeCacheClusters.Marker
}
internal.Cache.Set(cacheKey, clusters, cache.DefaultExpiration)
return clusters, nil
}
+40
View File
@@ -0,0 +1,40 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/elasticache"
elasticacheTypes "github.com/aws/aws-sdk-go-v2/service/elasticache/types"
)
type MockedElasticacheClient struct {
}
func (m *MockedElasticacheClient) DescribeCacheClusters(ctx context.Context, input *elasticache.DescribeCacheClustersInput, options ...func(*elasticache.Options)) (*elasticache.DescribeCacheClustersOutput, error) {
return &elasticache.DescribeCacheClustersOutput{
CacheClusters: []elasticacheTypes.CacheCluster{
{
CacheClusterId: aws.String("test"),
ARN: aws.String("arn:aws:elasticache:us-east-1:123456789012:cluster:myCluster"),
Engine: aws.String("redis"),
EngineVersion: aws.String("6.x"),
CacheNodeType: aws.String("cache.t3.micro"),
NumCacheNodes: aws.Int32(1),
CacheClusterStatus: aws.String("available"),
PreferredAvailabilityZone: aws.String("us-east-1a"),
CacheSubnetGroupName: aws.String("default"),
ReplicationGroupId: aws.String("test"),
SecurityGroups: []elasticacheTypes.SecurityGroupMembership{
{
SecurityGroupId: aws.String("test"),
Status: aws.String("active"),
},
},
AutoMinorVersionUpgrade: true,
PreferredMaintenanceWindow: aws.String("sun:05:00-sun:06:00"),
},
},
}, nil
}
+35
View File
@@ -0,0 +1,35 @@
package sdk
import (
"context"
"encoding/gob"
"github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk"
elasticbeanstalkTypes "github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk/types"
)
type AWSElasticBeanstalkClientInterface interface {
DescribeApplications(context.Context, *elasticbeanstalk.DescribeApplicationsInput, ...func(*elasticbeanstalk.Options)) (*elasticbeanstalk.DescribeApplicationsOutput, error)
}
func init() {
gob.Register([]elasticbeanstalkTypes.ApplicationDescription{})
}
func CachedElasticBeanstalkDescribeApplications(client AWSElasticBeanstalkClientInterface, accountID string, region string) ([]elasticbeanstalkTypes.ApplicationDescription, error) {
var applications []elasticbeanstalkTypes.ApplicationDescription
DescribeApplications, err := client.DescribeApplications(
context.TODO(),
&elasticbeanstalk.DescribeApplicationsInput{},
func(o *elasticbeanstalk.Options) {
o.Region = region
},
)
if err != nil {
return applications, err
}
applications = append(applications, DescribeApplications.Applications...)
return applications, nil
}
+33
View File
@@ -0,0 +1,33 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk"
elasticbeanstalkTypes "github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk/types"
)
type MockedElasticBeanstalkClient struct {
}
func (m *MockedElasticBeanstalkClient) DescribeApplications(ctx context.Context, input *elasticbeanstalk.DescribeApplicationsInput, options ...func(*elasticbeanstalk.Options)) (*elasticbeanstalk.DescribeApplicationsOutput, error) {
return &elasticbeanstalk.DescribeApplicationsOutput{
Applications: []elasticbeanstalkTypes.ApplicationDescription{
{
ApplicationName: aws.String("app1"),
ApplicationArn: aws.String("arn:aws:elasticbeanstalk:us-east-1:123456789012:application/app1"),
ConfigurationTemplates: []string{
"template1",
},
},
{
ApplicationName: aws.String("app2"),
ApplicationArn: aws.String("arn:aws:elasticbeanstalk:us-east-1:123456789012:application/app2"),
ConfigurationTemplates: []string{
"template2",
},
},
},
}, nil
}
+1 -1
View File
@@ -15,7 +15,7 @@ type ELBClientInterface interface {
DescribeLoadBalancers(context.Context, *elasticloadbalancing.DescribeLoadBalancersInput, ...func(*elasticloadbalancing.Options)) (*elasticloadbalancing.DescribeLoadBalancersOutput, error)
}
func RegisterELBTypes() {
func init() {
gob.Register([]elbTypes.LoadBalancerDescription{})
}
+37
View File
@@ -0,0 +1,37 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
elbTypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types"
)
type MockedElbClient struct {
}
func (m *MockedElbClient) DescribeLoadBalancers(ctx context.Context, input *elasticloadbalancing.DescribeLoadBalancersInput, options ...func(*elasticloadbalancing.Options)) (*elasticloadbalancing.DescribeLoadBalancersOutput, error) {
return &elasticloadbalancing.DescribeLoadBalancersOutput{
LoadBalancerDescriptions: []elbTypes.LoadBalancerDescription{
{
LoadBalancerName: aws.String("elb1"),
DNSName: aws.String("elb1"),
Instances: []elbTypes.Instance{
{
InstanceId: aws.String("i-1234567890abcdef0"),
},
},
},
{
LoadBalancerName: aws.String("elb2"),
DNSName: aws.String("elb2"),
Instances: []elbTypes.Instance{
{
InstanceId: aws.String("i-1234567890abcdef1"),
},
},
},
},
}, nil
}
+1 -1
View File
@@ -15,7 +15,7 @@ type ELBv2ClientInterface interface {
DescribeLoadBalancers(context.Context, *elasticloadbalancingv2.DescribeLoadBalancersInput, ...func(*elasticloadbalancingv2.Options)) (*elasticloadbalancingv2.DescribeLoadBalancersOutput, error)
}
func RegisterELBv2Types() {
func init() {
gob.Register([]elbV2Types.LoadBalancer{})
}
+58
View File
@@ -0,0 +1,58 @@
package sdk
import (
"context"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
elbv2Types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
)
type MockedElbv2Client struct {
}
func (m *MockedElbv2Client) DescribeLoadBalancers(ctx context.Context, input *elasticloadbalancingv2.DescribeLoadBalancersInput, options ...func(*elasticloadbalancingv2.Options)) (*elasticloadbalancingv2.DescribeLoadBalancersOutput, error) {
return &elasticloadbalancingv2.DescribeLoadBalancersOutput{
LoadBalancers: []elbv2Types.LoadBalancer{
{
LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188"),
DNSName: aws.String("my-load-balancer-424835706.us-east-1.elb.amazonaws.com"),
CanonicalHostedZoneId: aws.String("Z2P70J7EXAMPLE"),
CreatedTime: aws.Time(time.Now()),
LoadBalancerName: aws.String("my-load-balancer"),
Scheme: elbv2Types.LoadBalancerSchemeEnumInternetFacing,
VpcId: aws.String("vpc-3ac0fb5f"),
State: &elbv2Types.LoadBalancerState{
Code: elbv2Types.LoadBalancerStateEnumActive,
Reason: aws.String(""),
},
Type: elbv2Types.LoadBalancerTypeEnumApplication,
AvailabilityZones: []elbv2Types.AvailabilityZone{
{
ZoneName: aws.String("us-east-1a"),
SubnetId: aws.String("subnet-8360a9e7"),
LoadBalancerAddresses: []elbv2Types.LoadBalancerAddress{
{
IpAddress: aws.String("1.2.3.4"),
},
},
},
{
ZoneName: aws.String("us-east-1b"),
SubnetId: aws.String("subnet-b7d581c0"),
LoadBalancerAddresses: []elbv2Types.LoadBalancerAddress{
{
IpAddress: aws.String("2.3.4.5"),
},
},
},
},
SecurityGroups: []string{
"sg-5943793c",
},
IpAddressType: elbv2Types.IpAddressTypeDualstack,
},
},
}, nil
}
+98
View File
@@ -0,0 +1,98 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/emr"
emrTypes "github.com/aws/aws-sdk-go-v2/service/emr/types"
"github.com/patrickmn/go-cache"
)
type AWSEMRClientInterface interface {
ListClusters(context.Context, *emr.ListClustersInput, ...func(*emr.Options)) (*emr.ListClustersOutput, error)
ListInstances(context.Context, *emr.ListInstancesInput, ...func(*emr.Options)) (*emr.ListInstancesOutput, error)
}
func init() {
gob.Register([]emrTypes.ClusterSummary{})
//need to do this to avoid conflicts with the Instance type in the ec2 package
type EMRInstance emrTypes.Instance
gob.Register([]EMRInstance{})
}
func CachedEMRListClusters(client AWSEMRClientInterface, accountID string, region string) ([]emrTypes.ClusterSummary, error) {
var PaginationControl *string
var clusters []emrTypes.ClusterSummary
cacheKey := fmt.Sprintf("%s-emr-ListClusters-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]emrTypes.ClusterSummary), nil
}
for {
ListClusters, err := client.ListClusters(
context.TODO(),
&emr.ListClustersInput{
Marker: PaginationControl,
},
func(o *emr.Options) {
o.Region = region
},
)
if err != nil {
return clusters, err
}
clusters = append(clusters, ListClusters.Clusters...)
//pagination
if ListClusters.Marker == nil {
break
}
PaginationControl = ListClusters.Marker
}
internal.Cache.Set(cacheKey, clusters, cache.DefaultExpiration)
return clusters, nil
}
func CachedEMRListInstances(client AWSEMRClientInterface, accountID string, region string, clusterID string) ([]emrTypes.Instance, error) {
var PaginationControl *string
var instances []emrTypes.Instance
cacheKey := fmt.Sprintf("%s-emr-ListInstances-%s-%s", accountID, region, clusterID)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]emrTypes.Instance), nil
}
for {
ListInstances, err := client.ListInstances(
context.TODO(),
&emr.ListInstancesInput{
ClusterId: &clusterID,
Marker: PaginationControl,
},
func(o *emr.Options) {
o.Region = region
},
)
if err != nil {
return instances, err
}
instances = append(instances, ListInstances.Instances...)
//pagination
if ListInstances.Marker == nil {
break
}
PaginationControl = ListInstances.Marker
}
internal.Cache.Set(cacheKey, instances, cache.DefaultExpiration)
return instances, nil
}
+44
View File
@@ -0,0 +1,44 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/emr"
emrTypes "github.com/aws/aws-sdk-go-v2/service/emr/types"
)
type MockedEMRClient struct {
}
func (m *MockedEMRClient) ListClusters(ctx context.Context, input *emr.ListClustersInput, options ...func(*emr.Options)) (*emr.ListClustersOutput, error) {
return &emr.ListClustersOutput{
Clusters: []emrTypes.ClusterSummary{
{
Id: aws.String("cluster1"),
},
{
Id: aws.String("cluster2"),
},
},
}, nil
}
func (m *MockedEMRClient) ListInstances(ctx context.Context, input *emr.ListInstancesInput, options ...func(*emr.Options)) (*emr.ListInstancesOutput, error) {
return &emr.ListInstancesOutput{
Instances: []emrTypes.Instance{
{
Id: aws.String("instance1"),
InstanceType: aws.String("m5.xlarge"),
Ec2InstanceId: aws.String("i-1234567890"),
PrivateDnsName: aws.String("ip-10-0-0-1.ec2.internal"),
PublicDnsName: aws.String("ec2-1-2-3-4.compute-1.amazonaws.com"),
PrivateIpAddress: aws.String("10.0.0.1"),
PublicIpAddress: aws.String("1.2.3.4"),
},
{
Id: aws.String("instance2"),
},
},
}, nil
}
+192
View File
@@ -0,0 +1,192 @@
package sdk
import (
"context"
"encoding/gob"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/glue"
glueTypes "github.com/aws/aws-sdk-go-v2/service/glue/types"
"github.com/patrickmn/go-cache"
)
type AWSGlueClientInterface interface {
ListDevEndpoints(ctx context.Context, params *glue.ListDevEndpointsInput, optFns ...func(*glue.Options)) (*glue.ListDevEndpointsOutput, error)
ListJobs(ctx context.Context, params *glue.ListJobsInput, optFns ...func(*glue.Options)) (*glue.ListJobsOutput, error)
GetTables(ctx context.Context, params *glue.GetTablesInput, optFns ...func(*glue.Options)) (*glue.GetTablesOutput, error)
GetDatabases(ctx context.Context, params *glue.GetDatabasesInput, optFns ...func(*glue.Options)) (*glue.GetDatabasesOutput, error)
}
func init() {
gob.Register([]string{})
gob.Register(glueTypes.DevEndpoint{})
gob.Register(glueTypes.Job{})
gob.Register([]glueTypes.Table{})
gob.Register([]glueTypes.Database{})
}
func CachedGlueListDevEndpoints(GlueClient AWSGlueClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var devEndpoints []string
cacheKey := "glue-ListDevEndpoints-" + accountID + "-" + region
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached Glue dev endpoints data")
return cached.([]string), nil
}
for {
ListDevEndpoints, err := GlueClient.ListDevEndpoints(
context.TODO(),
&glue.ListDevEndpointsInput{
NextToken: PaginationControl,
},
func(o *glue.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
devEndpoints = append(devEndpoints, ListDevEndpoints.DevEndpointNames...)
// Pagination control.
if aws.ToString(ListDevEndpoints.NextToken) != "" {
PaginationControl = ListDevEndpoints.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, devEndpoints, cache.DefaultExpiration)
return devEndpoints, nil
}
func CachedGlueListJobs(GlueClient AWSGlueClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var jobs []string
cacheKey := "glue-ListJobs-" + accountID + "-" + region
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached Glue jobs data")
return cached.([]string), nil
}
for {
ListJobs, err := GlueClient.ListJobs(
context.TODO(),
&glue.ListJobsInput{
NextToken: PaginationControl,
},
func(o *glue.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
jobs = append(jobs, ListJobs.JobNames...)
// Pagination control.
if ListJobs.NextToken != nil {
PaginationControl = ListJobs.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, jobs, cache.DefaultExpiration)
return jobs, nil
}
func CachedGlueGetTables(GlueClient AWSGlueClientInterface, accountID string, region string, dbName string) ([]glueTypes.Table, error) {
var PaginationControl *string
var tables []glueTypes.Table
cacheKey := "glue-GetTables-" + accountID + "-" + region
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached Glue tables data")
return cached.([]glueTypes.Table), nil
}
for {
GetTables, err := GlueClient.GetTables(
context.TODO(),
&glue.GetTablesInput{
DatabaseName: &dbName,
NextToken: PaginationControl,
},
func(o *glue.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
tables = append(tables, GetTables.TableList...)
// Pagination control.
if GetTables.NextToken != nil {
PaginationControl = GetTables.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, tables, cache.DefaultExpiration)
return tables, nil
}
func CachedGlueGetDatabases(GlueClient AWSGlueClientInterface, accountID string, region string) ([]glueTypes.Database, error) {
var PaginationControl *string
var databases []glueTypes.Database
cacheKey := "glue-GetDatabases-" + accountID + "-" + region
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached Glue databases data")
return cached.([]glueTypes.Database), nil
}
for {
GetDatabases, err := GlueClient.GetDatabases(
context.TODO(),
&glue.GetDatabasesInput{
NextToken: PaginationControl,
},
func(o *glue.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
databases = append(databases, GetDatabases.DatabaseList...)
// Pagination control.
if GetDatabases.NextToken != nil {
PaginationControl = GetDatabases.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, databases, cache.DefaultExpiration)
return databases, nil
}
+80
View File
@@ -0,0 +1,80 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/glue"
glueTypes "github.com/aws/aws-sdk-go-v2/service/glue/types"
)
type MockedGlueClient struct {
}
func (m *MockedGlueClient) ListDevEndpoints(ctx context.Context, input *glue.ListDevEndpointsInput, options ...func(*glue.Options)) (*glue.ListDevEndpointsOutput, error) {
return &glue.ListDevEndpointsOutput{
DevEndpointNames: []string{
"devendpoint1",
"devendpoint2",
},
}, nil
}
func (m *MockedGlueClient) ListJobs(ctx context.Context, input *glue.ListJobsInput, options ...func(*glue.Options)) (*glue.ListJobsOutput, error) {
return &glue.ListJobsOutput{
JobNames: []string{
"job1",
"job2",
},
}, nil
}
func (m *MockedGlueClient) GetTables(ctx context.Context, input *glue.GetTablesInput, options ...func(*glue.Options)) (*glue.GetTablesOutput, error) {
return &glue.GetTablesOutput{
TableList: []glueTypes.Table{
{
Name: aws.String("table1"),
DatabaseName: aws.String("database1"),
Description: aws.String("description1"),
Parameters: map[string]string{
"param1": "value1",
"param2": "value2",
},
},
{
Name: aws.String("table2"),
DatabaseName: aws.String("database2"),
Description: aws.String("description2"),
Parameters: map[string]string{
"param1": "value1",
"param2": "value2",
},
},
},
}, nil
}
func (m *MockedGlueClient) GetDatabases(ctx context.Context, input *glue.GetDatabasesInput, options ...func(*glue.Options)) (*glue.GetDatabasesOutput, error) {
return &glue.GetDatabasesOutput{
DatabaseList: []glueTypes.Database{
{
Name: aws.String("database1"),
Description: aws.String("description1"),
LocationUri: aws.String("s3://bucket1"),
Parameters: map[string]string{
"param1": "value1",
"param2": "value2",
},
},
{
Name: aws.String("database2"),
Description: aws.String("description2"),
LocationUri: aws.String("s3://bucket2"),
Parameters: map[string]string{
"param1": "value1",
"param2": "value2",
},
},
},
}, nil
}
+1 -1
View File
@@ -15,7 +15,7 @@ type GrafanaClientInterface interface {
ListWorkspaces(context.Context, *grafana.ListWorkspacesInput, ...func(*grafana.Options)) (*grafana.ListWorkspacesOutput, error)
}
func RegisterGrafanaTypes() {
func init() {
gob.Register([]grafanaTypes.WorkspaceSummary{})
}
+42
View File
@@ -0,0 +1,42 @@
package sdk
import (
"context"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/grafana"
grafanaTypes "github.com/aws/aws-sdk-go-v2/service/grafana/types"
)
type MockedGrafanaClient struct {
}
func (m *MockedGrafanaClient) ListWorkspaces(ctx context.Context, input *grafana.ListWorkspacesInput, options ...func(*grafana.Options)) (*grafana.ListWorkspacesOutput, error) {
return &grafana.ListWorkspacesOutput{
Workspaces: []grafanaTypes.WorkspaceSummary{
{
Authentication: &grafanaTypes.AuthenticationSummary{
Providers: []grafanaTypes.AuthenticationProviderTypes{
grafanaTypes.AuthenticationProviderTypesAwsSso,
},
},
Created: aws.Time(time.Now()),
Id: aws.String("workspace1"),
Name: aws.String("workspace1"),
Status: grafanaTypes.WorkspaceStatusActive,
},
{
Authentication: &grafanaTypes.AuthenticationSummary{
Providers: []grafanaTypes.AuthenticationProviderTypes{
grafanaTypes.AuthenticationProviderTypesAwsSso,
},
},
Created: aws.Time(time.Now()),
Id: aws.String("workspace2"),
Name: aws.String("workspace2"),
Status: grafanaTypes.WorkspaceStatusActive,
},
},
}, nil
}
+39 -1
View File
@@ -22,12 +22,14 @@ type AWSIAMClientInterface interface {
GetAccountAuthorizationDetails(ctx context.Context, params *iam.GetAccountAuthorizationDetailsInput, optFns ...func(*iam.Options)) (*iam.GetAccountAuthorizationDetailsOutput, error)
SimulatePrincipalPolicy(ctx context.Context, params *iam.SimulatePrincipalPolicyInput, optFns ...func(*iam.Options)) (*iam.SimulatePrincipalPolicyOutput, error)
ListInstanceProfiles(ctx context.Context, params *iam.ListInstanceProfilesInput, optFns ...func(*iam.Options)) (*iam.ListInstanceProfilesOutput, error)
ListGroups(ctx context.Context, params *iam.ListGroupsInput, optFns ...func(*iam.Options)) (*iam.ListGroupsOutput, error)
}
func RegisterIamTypes() {
func init() {
gob.Register([]iamTypes.User{})
gob.Register([]iamTypes.AccessKeyMetadata{})
gob.Register([]iamTypes.Role{})
gob.Register([]iamTypes.Group{})
gob.Register([]iamTypes.PolicyDetail{})
gob.Register([]iamTypes.InstanceProfile{})
gob.Register([]iamTypes.EvaluationResult{})
@@ -250,3 +252,39 @@ func CachedIamSimulatePrincipalPolicy(IAMClient AWSIAMClientInterface, accountID
internal.Cache.Set(cacheKey, EvaluationResults, cache.DefaultExpiration)
return EvaluationResults, nil
}
func CachedIamListGroups(IAMClient AWSIAMClientInterface, accountID string) ([]iamTypes.Group, error) {
var PaginationControl *string
var Groups []iamTypes.Group
cacheKey := fmt.Sprintf("%s-iam-ListGroups", accountID)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]iamTypes.Group), nil
}
for {
ListGroups, err := IAMClient.ListGroups(
context.TODO(),
&iam.ListGroupsInput{
Marker: PaginationControl,
},
)
if err != nil {
return Groups, err
}
Groups = append(Groups, ListGroups.Groups...)
// Pagination control.
if ListGroups.Marker != nil {
PaginationControl = ListGroups.Marker
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, Groups, cache.DefaultExpiration)
return Groups, nil
}
+222
View File
@@ -0,0 +1,222 @@
package sdk
import (
"context"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
iamTypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
)
type MockedIAMClient struct {
}
func (m *MockedIAMClient) ListUsers(ctx context.Context, input *iam.ListUsersInput, options ...func(*iam.Options)) (*iam.ListUsersOutput, error) {
return &iam.ListUsersOutput{
Users: []iamTypes.User{
{
Arn: aws.String("arn:aws:iam::123456789012:user/user1"),
CreateDate: aws.Time(time.Now()),
Path: aws.String("/"),
UserId: aws.String("123456789012"),
UserName: aws.String("user1"),
},
{
Arn: aws.String("arn:aws:iam::123456789012:user/user2"),
CreateDate: aws.Time(time.Now()),
Path: aws.String("/"),
UserId: aws.String("123456789012"),
UserName: aws.String("user2"),
},
},
}, nil
}
func (m *MockedIAMClient) ListAccessKeys(ctx context.Context, input *iam.ListAccessKeysInput, options ...func(*iam.Options)) (*iam.ListAccessKeysOutput, error) {
return &iam.ListAccessKeysOutput{
AccessKeyMetadata: []iamTypes.AccessKeyMetadata{
{
AccessKeyId: aws.String("accesskey1"),
CreateDate: aws.Time(time.Now()),
Status: iamTypes.StatusTypeActive,
UserName: aws.String("user1"),
},
{
AccessKeyId: aws.String("accesskey2"),
CreateDate: aws.Time(time.Now()),
Status: iamTypes.StatusTypeActive,
UserName: aws.String("user2"),
},
},
}, nil
}
func (m *MockedIAMClient) ListGroups(ctx context.Context, input *iam.ListGroupsInput, options ...func(*iam.Options)) (*iam.ListGroupsOutput, error) {
return &iam.ListGroupsOutput{
Groups: []iamTypes.Group{
{
Arn: aws.String("arn:aws:iam::123456789012:group/group1"),
CreateDate: aws.Time(time.Now()),
GroupId: aws.String("123456789012"),
GroupName: aws.String("group1"),
Path: aws.String("/"),
},
{
Arn: aws.String("arn:aws:iam::123456789012:group/group2"),
CreateDate: aws.Time(time.Now()),
GroupId: aws.String("123456789012"),
GroupName: aws.String("group2"),
Path: aws.String("/"),
},
},
}, nil
}
func (m *MockedIAMClient) ListRoles(ctx context.Context, input *iam.ListRolesInput, options ...func(*iam.Options)) (*iam.ListRolesOutput, error) {
return &iam.ListRolesOutput{
Roles: []iamTypes.Role{
{
Arn: aws.String("arn:aws:iam::123456789012:role/role1"),
AssumeRolePolicyDocument: aws.String("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"),
CreateDate: aws.Time(time.Now()),
RoleId: aws.String("123456789012"),
RoleName: aws.String("role1"),
Path: aws.String("/"),
},
{
Arn: aws.String("arn:aws:iam::123456789012:role/role2"),
AssumeRolePolicyDocument: aws.String("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"),
CreateDate: aws.Time(time.Now()),
RoleId: aws.String("123456789012"),
RoleName: aws.String("role2"),
Path: aws.String("/"),
},
{
Arn: aws.String("arn:aws:iam::123456789012:role/role3"),
AssumeRolePolicyDocument: aws.String("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"},\"Action\":\"sts:AssumeRole\"}]}"),
CreateDate: aws.Time(time.Now()),
RoleId: aws.String("123456789012"),
RoleName: aws.String("role3"),
Path: aws.String("/"),
},
},
}, nil
}
func (m *MockedIAMClient) GetAccountAuthorizationDetails(ctx context.Context, params *iam.GetAccountAuthorizationDetailsInput, optFns ...func(*iam.Options)) (*iam.GetAccountAuthorizationDetailsOutput, error) {
return &iam.GetAccountAuthorizationDetailsOutput{
GroupDetailList: []iamTypes.GroupDetail{
{
Arn: aws.String("arn:aws:iam::123456789012:group/group1"),
CreateDate: aws.Time(time.Now()),
GroupId: aws.String("123456789012"),
GroupName: aws.String("group1"),
Path: aws.String("/"),
},
{
Arn: aws.String("arn:aws:iam::123456789012:group/group2"),
CreateDate: aws.Time(time.Now()),
GroupId: aws.String("123456789012"),
GroupName: aws.String("group2"),
Path: aws.String("/"),
},
},
RoleDetailList: []iamTypes.RoleDetail{
{
Arn: aws.String("arn:aws:iam::123456789012:role/role1"),
AssumeRolePolicyDocument: aws.String("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"),
CreateDate: aws.Time(time.Now()),
RoleId: aws.String("123456789012"),
RoleName: aws.String("role1"),
Path: aws.String("/"),
},
{
Arn: aws.String("arn:aws:iam::123456789012:role/role2"),
AssumeRolePolicyDocument: aws.String("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"),
CreateDate: aws.Time(time.Now()),
RoleId: aws.String("123456789012"),
RoleName: aws.String("role2"),
Path: aws.String("/"),
},
{
Arn: aws.String("arn:aws:iam::123456789012:role/role3"),
AssumeRolePolicyDocument: aws.String("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"},\"Action\":\"sts:AssumeRole\"}]}"),
CreateDate: aws.Time(time.Now()),
RoleId: aws.String("123456789012"),
RoleName: aws.String("role3"),
Path: aws.String("/"),
},
},
UserDetailList: []iamTypes.UserDetail{
{
Arn: aws.String("arn:aws:iam::123456789012:user/user1"),
CreateDate: aws.Time(time.Now()),
UserId: aws.String("123456789012"),
UserName: aws.String("user1"),
Path: aws.String("/"),
},
{
Arn: aws.String("arn:aws:iam::123456789012:user/user2"),
CreateDate: aws.Time(time.Now()),
UserId: aws.String("123456789012"),
UserName: aws.String("user2"),
Path: aws.String("/"),
},
},
}, nil
}
func (m *MockedIAMClient) SimulatePrincipalPolicy(ctx context.Context, params *iam.SimulatePrincipalPolicyInput, optFns ...func(*iam.Options)) (*iam.SimulatePrincipalPolicyOutput, error) {
return &iam.SimulatePrincipalPolicyOutput{
EvaluationResults: []iamTypes.EvaluationResult{
{
EvalActionName: aws.String("sts:AssumeRole"),
EvalDecision: iamTypes.PolicyEvaluationDecisionTypeAllowed,
EvalResourceName: aws.String("arn:aws:iam::123456789012:role/role1"),
MatchedStatements: []iamTypes.Statement{
{
SourcePolicyId: aws.String("PolicyForRole1"),
SourcePolicyType: iamTypes.PolicySourceTypeUser,
StartPosition: &iamTypes.Position{
Column: 0,
Line: 0,
},
},
},
},
},
}, nil
}
func (m *MockedIAMClient) ListInstanceProfiles(ctx context.Context, params *iam.ListInstanceProfilesInput, optFns ...func(*iam.Options)) (*iam.ListInstanceProfilesOutput, error) {
return &iam.ListInstanceProfilesOutput{
InstanceProfiles: []iamTypes.InstanceProfile{
{
Arn: aws.String("arn:aws:iam::123456789012:instance-profile/instance-profile1"),
CreateDate: aws.Time(time.Now()),
InstanceProfileId: aws.String("123456789012"),
InstanceProfileName: aws.String("instance-profile1"),
Path: aws.String("/"),
Roles: []iamTypes.Role{
{
Arn: aws.String("arn:aws:iam::123456789012:role/role1"),
AssumeRolePolicyDocument: aws.String("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"),
CreateDate: aws.Time(time.Now()),
RoleId: aws.String("123456789012"),
RoleName: aws.String("role1"),
Path: aws.String("/"),
},
},
},
},
}, nil
}
+55
View File
@@ -0,0 +1,55 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/kinesis"
"github.com/patrickmn/go-cache"
)
type AWSKinesisClientInterface interface {
ListStreams(context.Context, *kinesis.ListStreamsInput, ...func(*kinesis.Options)) (*kinesis.ListStreamsOutput, error)
}
func init() {
gob.Register([]string{})
}
func CachedKinesisListStreams(client AWSKinesisClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var streams []string
cacheKey := fmt.Sprintf("%s-kinesis-ListStreams-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]string), nil
}
for {
ListStreams, err := client.ListStreams(
context.TODO(),
&kinesis.ListStreamsInput{
NextToken: PaginationControl,
},
func(o *kinesis.Options) {
o.Region = region
},
)
if err != nil {
return streams, err
}
streams = append(streams, ListStreams.StreamNames...)
//pagination
if ListStreams.NextToken == nil {
break
}
PaginationControl = ListStreams.NextToken
}
internal.Cache.Set(cacheKey, streams, cache.DefaultExpiration)
return streams, nil
}
+21
View File
@@ -0,0 +1,21 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/kinesis"
)
type MockedKinesisClient struct {
}
func (m *MockedKinesisClient) ListStreams(ctx context.Context, input *kinesis.ListStreamsInput, options ...func(*kinesis.Options)) (*kinesis.ListStreamsOutput, error) {
return &kinesis.ListStreamsOutput{
HasMoreStreams: aws.Bool(false),
StreamNames: []string{
"stream1",
"stream2",
},
}, nil
}
+1 -1
View File
@@ -17,7 +17,7 @@ type LambdaClientInterface interface {
GetFunctionUrlConfig(context.Context, *lambda.GetFunctionUrlConfigInput, ...func(*lambda.Options)) (*lambda.GetFunctionUrlConfigOutput, error)
}
func RegisterLambdaTypes() {
func init() {
gob.Register([]lambdaTypes.FunctionConfiguration{})
gob.Register(customGetFuntionURLOutput{})
}
+62
View File
@@ -0,0 +1,62 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/lambda"
lambdaTypes "github.com/aws/aws-sdk-go-v2/service/lambda/types"
)
type MockedLambdaClient struct {
}
func (m *MockedLambdaClient) ListFunctions(ctx context.Context, input *lambda.ListFunctionsInput, options ...func(*lambda.Options)) (*lambda.ListFunctionsOutput, error) {
return &lambda.ListFunctionsOutput{
Functions: []lambdaTypes.FunctionConfiguration{
{
FunctionArn: aws.String("arn:aws:lambda:us-east-1:123456789012:function:my-function"),
FunctionName: aws.String("my-function"),
Handler: aws.String("index.handler"),
Runtime: lambdaTypes.RuntimeNodejs18x,
Environment: &lambdaTypes.EnvironmentResponse{
Variables: map[string]string{
"key1": "value1",
"key2": "value2",
},
},
},
{
FunctionArn: aws.String("arn:aws:lambda:us-east-1:123456789012:function:my-function2"),
FunctionName: aws.String("my-function2"),
Handler: aws.String("index.handler"),
Runtime: lambdaTypes.RuntimeNodejs18x,
},
},
}, nil
}
func (m *MockedLambdaClient) GetFunction(ctx context.Context, input *lambda.GetFunctionInput, options ...func(*lambda.Options)) (*lambda.GetFunctionOutput, error) {
return &lambda.GetFunctionOutput{
Configuration: &lambdaTypes.FunctionConfiguration{
FunctionArn: aws.String("arn:aws:lambda:us-east-1:123456789012:function:my-function"),
FunctionName: aws.String("my-function"),
Handler: aws.String("index.handler"),
Runtime: lambdaTypes.RuntimeNodejs18x,
Environment: &lambdaTypes.EnvironmentResponse{
Variables: map[string]string{
"key1": "value1",
"key2": "value2",
},
},
},
}, nil
}
func (m *MockedLambdaClient) GetFunctionUrlConfig(ctx context.Context, input *lambda.GetFunctionInput, options ...func(*lambda.Options)) (*lambda.GetFunctionUrlConfigOutput, error) {
return &lambda.GetFunctionUrlConfigOutput{
FunctionUrl: aws.String("https://my-function.us-east-1.amazonaws.com/Prod/"),
FunctionArn: aws.String("arn:aws:lambda:us-east-1:123456789012:function:my-function"),
AuthType: lambdaTypes.FunctionUrlAuthTypeNone,
}, nil
}
+6 -2
View File
@@ -16,8 +16,12 @@ type lightsailClientInterface interface {
GetContainerServices(context.Context, *lightsail.GetContainerServicesInput, ...func(*lightsail.Options)) (*lightsail.GetContainerServicesOutput, error)
}
func RegisterLightsailTypes() {
//gob.Register([]lightsailTypes.Instance{})
func init() {
//need to do this to avoid conflicts with the Instance type in the ec2 package
type lightsailInstance lightsailTypes.Instance
gob.Register([]lightsailInstance{})
gob.Register([]lightsailTypes.ContainerService{})
}
+80
View File
@@ -0,0 +1,80 @@
package sdk
import (
"context"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/lightsail"
lightsailTypes "github.com/aws/aws-sdk-go-v2/service/lightsail/types"
)
type MockedLightsailClient struct {
}
func (m *MockedLightsailClient) GetInstances(ctx context.Context, input *lightsail.GetInstancesInput, options ...func(*lightsail.Options)) (*lightsail.GetInstancesOutput, error) {
return &lightsail.GetInstancesOutput{
Instances: []lightsailTypes.Instance{
{
BlueprintId: aws.String("blueprint1"),
BundleId: aws.String("bundle1"),
CreatedAt: aws.Time(time.Now()),
Location: &lightsailTypes.ResourceLocation{
AvailabilityZone: aws.String("us-east-1a"),
RegionName: lightsailTypes.RegionNameUsEast1,
},
Name: aws.String("instance1"),
Networking: &lightsailTypes.InstanceNetworking{
MonthlyTransfer: &lightsailTypes.MonthlyTransfer{
GbPerMonthAllocated: aws.Int32(1),
},
},
PrivateIpAddress: aws.String("10.1.1.1"),
PublicIpAddress: aws.String("1.2.3.4"),
},
{
BlueprintId: aws.String("blueprint2"),
BundleId: aws.String("bundle2"),
CreatedAt: aws.Time(time.Now()),
Location: &lightsailTypes.ResourceLocation{
AvailabilityZone: aws.String("us-east-1b"),
RegionName: lightsailTypes.RegionNameUsEast1,
},
Name: aws.String("instance2"),
Networking: &lightsailTypes.InstanceNetworking{
MonthlyTransfer: &lightsailTypes.MonthlyTransfer{
GbPerMonthAllocated: aws.Int32(2),
},
},
PrivateIpAddress: aws.String("10.2.2.2"),
PublicIpAddress: aws.String("2.3.4.4"),
},
},
}, nil
}
func (m *MockedLightsailClient) GetContainerServices(ctx context.Context, input *lightsail.GetContainerServicesInput, options ...func(*lightsail.Options)) (*lightsail.GetContainerServicesOutput, error) {
return &lightsail.GetContainerServicesOutput{
ContainerServices: []lightsailTypes.ContainerService{
{
Arn: aws.String("arn1"),
Location: &lightsailTypes.ResourceLocation{
AvailabilityZone: aws.String("us-east-1a"),
RegionName: lightsailTypes.RegionNameUsEast1,
},
Url: aws.String("https://container1"),
PrivateDomainName: aws.String("container1"),
},
{
Arn: aws.String("arn2"),
Location: &lightsailTypes.ResourceLocation{
AvailabilityZone: aws.String("us-east-1a"),
RegionName: lightsailTypes.RegionNameUsEast1,
},
Url: aws.String("https://container2"),
PrivateDomainName: aws.String("container2"),
},
},
}, nil
}
+1 -1
View File
@@ -15,7 +15,7 @@ type MQClientInterface interface {
ListBrokers(context.Context, *mq.ListBrokersInput, ...func(*mq.Options)) (*mq.ListBrokersOutput, error)
}
func RegisterMQTypes() {
func init() {
gob.Register([]mqTypes.BrokerSummary{})
}
+40
View File
@@ -0,0 +1,40 @@
package sdk
import (
"context"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/mq"
mqTypes "github.com/aws/aws-sdk-go-v2/service/mq/types"
)
type MockedMQClient struct {
}
func (m *MockedMQClient) ListBrokers(ctx context.Context, input *mq.ListBrokersInput, options ...func(*mq.Options)) (*mq.ListBrokersOutput, error) {
return &mq.ListBrokersOutput{
BrokerSummaries: []mqTypes.BrokerSummary{
{
BrokerArn: aws.String("broker1"),
BrokerId: aws.String("broker1"),
BrokerName: aws.String("broker1"),
BrokerState: mqTypes.BrokerStateRunning,
Created: aws.Time(time.Now()),
DeploymentMode: mqTypes.DeploymentModeSingleInstance,
EngineType: mqTypes.EngineTypeRabbitmq,
HostInstanceType: aws.String("host1"),
},
{
BrokerArn: aws.String("broker2"),
BrokerId: aws.String("broker2"),
BrokerName: aws.String("broker2"),
BrokerState: mqTypes.BrokerStateRunning,
Created: aws.Time(time.Now()),
DeploymentMode: mqTypes.DeploymentModeSingleInstance,
EngineType: mqTypes.EngineTypeActivemq,
HostInstanceType: aws.String("host2"),
},
},
}, nil
}
+31 -1
View File
@@ -14,11 +14,13 @@ import (
type OpenSearchClientInterface interface {
ListDomainNames(context.Context, *opensearch.ListDomainNamesInput, ...func(*opensearch.Options)) (*opensearch.ListDomainNamesOutput, error)
DescribeDomainConfig(context.Context, *opensearch.DescribeDomainConfigInput, ...func(*opensearch.Options)) (*opensearch.DescribeDomainConfigOutput, error)
DescribeDomain(context.Context, *opensearch.DescribeDomainInput, ...func(*opensearch.Options)) (*opensearch.DescribeDomainOutput, error)
}
func RegisterOpenSearchTypes() {
func init() {
gob.Register([]openSearchTypes.DomainInfo{})
gob.Register(openSearchTypes.DomainConfig{})
gob.Register(openSearchTypes.DomainStatus{})
}
// create CachedOpenSearchListDomainNames function that uses go-cache and pagination
@@ -75,3 +77,31 @@ func CachedOpenSearchDescribeDomainConfig(client OpenSearchClientInterface, acco
internal.Cache.Set(cacheKey, DomainConfig, cache.DefaultExpiration)
return DomainConfig, nil
}
// create CachedOpenSearchDescribeDomain function that uses go-cache and pagination and supports region option
func CachedOpenSearchDescribeDomain(client OpenSearchClientInterface, accountID string, region string, domainName string) (openSearchTypes.DomainStatus, error) {
var DomainStatus openSearchTypes.DomainStatus
cacheKey := fmt.Sprintf("%s-opensearch-DescribeDomain-%s-%s", accountID, region, domainName)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.(openSearchTypes.DomainStatus), nil
}
DescribeDomain, err := client.DescribeDomain(
context.TODO(),
&opensearch.DescribeDomainInput{
DomainName: &domainName,
},
func(o *opensearch.Options) {
o.Region = region
},
)
if err != nil {
return DomainStatus, err
}
DomainStatus = *DescribeDomain.DomainStatus
internal.Cache.Set(cacheKey, DomainStatus, cache.DefaultExpiration)
return DomainStatus, nil
}
+63
View File
@@ -0,0 +1,63 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/opensearch"
openSearchTypes "github.com/aws/aws-sdk-go-v2/service/opensearch/types"
)
type MockedOpenSearchClient struct {
}
func (m *MockedOpenSearchClient) ListDomainNames(ctx context.Context, input *opensearch.ListDomainNamesInput, options ...func(*opensearch.Options)) (*opensearch.ListDomainNamesOutput, error) {
return &opensearch.ListDomainNamesOutput{
DomainNames: []openSearchTypes.DomainInfo{
{
DomainName: aws.String("domain1"),
EngineType: openSearchTypes.EngineTypeOpenSearch,
},
{
DomainName: aws.String("domain2"),
EngineType: openSearchTypes.EngineTypeElasticsearch,
},
},
}, nil
}
func (m *MockedOpenSearchClient) DescribeDomainConfig(ctx context.Context, input *opensearch.DescribeDomainConfigInput, options ...func(*opensearch.Options)) (*opensearch.DescribeDomainConfigOutput, error) {
return &opensearch.DescribeDomainConfigOutput{
DomainConfig: &openSearchTypes.DomainConfig{
EngineVersion: &openSearchTypes.VersionStatus{
Options: aws.String("OpenSearch-1.1"),
Status: &openSearchTypes.OptionStatus{
PendingDeletion: aws.Bool(false),
},
},
ClusterConfig: &openSearchTypes.ClusterConfigStatus{
Options: &openSearchTypes.ClusterConfig{
DedicatedMasterCount: aws.Int32(3),
DedicatedMasterEnabled: aws.Bool(true),
InstanceCount: aws.Int32(3),
WarmCount: aws.Int32(3),
WarmEnabled: aws.Bool(true),
},
},
DomainEndpointOptions: &openSearchTypes.DomainEndpointOptionsStatus{
Options: &openSearchTypes.DomainEndpointOptions{
EnforceHTTPS: aws.Bool(true),
},
},
},
}, nil
}
func (m *MockedOpenSearchClient) DescribeDomain(ctx context.Context, input *opensearch.DescribeDomainInput, options ...func(*opensearch.Options)) (*opensearch.DescribeDomainOutput, error) {
return &opensearch.DescribeDomainOutput{
DomainStatus: &openSearchTypes.DomainStatus{
DomainName: aws.String("domain1"),
Endpoint: aws.String("https://domain1.us-east-1.es.amazonaws.com"),
},
}, nil
}
+1 -1
View File
@@ -16,7 +16,7 @@ type OrganizationsClientInterface interface {
DescribeOrganization(ctx context.Context, params *organizations.DescribeOrganizationInput, optFns ...func(*organizations.Options)) (*organizations.DescribeOrganizationOutput, error)
}
func RegisterOrganizationsTypes() {
func init() {
gob.Register([]orgTypes.Account{})
//gob.Register(orgTypes.Organization{})
}
+52
View File
@@ -0,0 +1,52 @@
package sdk
import (
"context"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/organizations"
organizationsTypes "github.com/aws/aws-sdk-go-v2/service/organizations/types"
)
type MockedOrgClient struct {
}
func (m *MockedOrgClient) ListAccounts(ctx context.Context, input *organizations.ListAccountsInput, options ...func(*organizations.Options)) (*organizations.ListAccountsOutput, error) {
return &organizations.ListAccountsOutput{
Accounts: []organizationsTypes.Account{
{
Arn: aws.String("arn:aws:organizations::123456789012:account/o-exampleorgid/111111111111"),
Email: aws.String("[email protected]"),
Id: aws.String("111111111111"),
JoinedMethod: organizationsTypes.AccountJoinedMethodInvited,
JoinedTimestamp: aws.Time(time.Now()),
Name: aws.String("account1"),
Status: organizationsTypes.AccountStatusActive,
},
{
Arn: aws.String("arn:aws:organizations::123456789012:account/o-exampleorgid/222222222222"),
Email: aws.String("[email protected]"),
Id: aws.String("222222222222"),
JoinedMethod: organizationsTypes.AccountJoinedMethodCreated,
JoinedTimestamp: aws.Time(time.Now()),
Name: aws.String("account2"),
Status: organizationsTypes.AccountStatusActive,
},
},
}, nil
}
func (m *MockedOrgClient) DescribeOrganization(ctx context.Context, input *organizations.DescribeOrganizationInput, options ...func(*organizations.Options)) (*organizations.DescribeOrganizationOutput, error) {
return &organizations.DescribeOrganizationOutput{
Organization: &organizationsTypes.Organization{
Arn: aws.String("arn:aws:organizations::123456789012:organization/o-exampleorgid"),
FeatureSet: organizationsTypes.OrganizationFeatureSetAll,
Id: aws.String("o-exampleorgid"),
MasterAccountArn: aws.String("arn:aws:organizations::123456789012:account/o-exampleorgid/111111111111"),
MasterAccountEmail: aws.String("[email protected]"),
MasterAccountId: aws.String("111111111111"),
},
}, nil
}
+1 -1
View File
@@ -16,7 +16,7 @@ type RDSClientInterface interface {
DescribeDBInstances(context.Context, *rds.DescribeDBInstancesInput, ...func(*rds.Options)) (*rds.DescribeDBInstancesOutput, error)
}
func RegisterRDSTypes() {
func init() {
gob.Register([]rdsTypes.DBInstance{})
}
+34
View File
@@ -0,0 +1,34 @@
package sdk
import (
"context"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rds"
rdsTypes "github.com/aws/aws-sdk-go-v2/service/rds/types"
)
type MOckedRDSClient struct {
}
func (m *MOckedRDSClient) DescribeDBInstances(ctx context.Context, input *rds.DescribeDBInstancesInput, options ...func(*rds.Options)) (*rds.DescribeDBInstancesOutput, error) {
return &rds.DescribeDBInstancesOutput{
DBInstances: []rdsTypes.DBInstance{
{
DBInstanceIdentifier: aws.String("db1"),
Engine: aws.String("postgres"),
EngineVersion: aws.String("13.3"),
InstanceCreateTime: aws.Time(time.Now()),
MasterUsername: aws.String("postgres"),
},
{
DBInstanceIdentifier: aws.String("db2"),
Engine: aws.String("postgres"),
EngineVersion: aws.String("13.3"),
InstanceCreateTime: aws.Time(time.Now()),
MasterUsername: aws.String("postgres"),
},
},
}, nil
}
+4 -3
View File
@@ -11,15 +11,16 @@ import (
"github.com/patrickmn/go-cache"
)
type RedShiftClientInterface interface {
type AWSRedShiftClientInterface interface {
DescribeClusters(context.Context, *redshift.DescribeClustersInput, ...func(*redshift.Options)) (*redshift.DescribeClustersOutput, error)
}
func RegisterRedShiftTypes() {
func init() {
gob.Register([]redshiftTypes.Cluster{})
}
func CachedRedShiftDescribeClusters(client RedShiftClientInterface, accountID string, region string) ([]redshiftTypes.Cluster, error) {
func CachedRedShiftDescribeClusters(client AWSRedShiftClientInterface, accountID string, region string) ([]redshiftTypes.Cluster, error) {
var PaginationControl *string
var clusters []redshiftTypes.Cluster
cacheKey := fmt.Sprintf("%s-redshift-DescribeClusters-%s", accountID, region)
+37
View File
@@ -0,0 +1,37 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/redshift"
redshiftTypes "github.com/aws/aws-sdk-go-v2/service/redshift/types"
)
type MockedRedshiftClient struct {
}
func (m *MockedRedshiftClient) DescribeClusters(ctx context.Context, input *redshift.DescribeClustersInput, options ...func(*redshift.Options)) (*redshift.DescribeClustersOutput, error) {
return &redshift.DescribeClustersOutput{
Clusters: []redshiftTypes.Cluster{
{
ClusterIdentifier: aws.String("cluster1"),
ClusterNamespaceArn: aws.String("arn:aws:redshift:us-east-1:123456789012:cluster:cluster1"),
DBName: aws.String("db1"),
Endpoint: &redshiftTypes.Endpoint{
Address: aws.String("cluster1.us-east-1.redshift.amazonaws.com"),
Port: 5439,
},
},
{
ClusterIdentifier: aws.String("cluster2"),
ClusterNamespaceArn: aws.String("arn:aws:redshift:us-east-1:123456789012:cluster:cluster2"),
DBName: aws.String("db2"),
Endpoint: &redshiftTypes.Endpoint{
Address: aws.String("cluster2.us-east-1.redshift.amazonaws.com"),
Port: 5439,
},
},
},
}, nil
}
+92
View File
@@ -0,0 +1,92 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/route53"
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
"github.com/patrickmn/go-cache"
)
type AWSRoute53ClientInterface interface {
ListHostedZones(context.Context, *route53.ListHostedZonesInput, ...func(*route53.Options)) (*route53.ListHostedZonesOutput, error)
ListResourceRecordSets(context.Context, *route53.ListResourceRecordSetsInput, ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error)
}
func init() {
gob.Register([]route53types.HostedZone{})
gob.Register([]route53types.ResourceRecordSet{})
}
func CachedRoute53ListHostedZones(client AWSRoute53ClientInterface, accountID string) ([]route53types.HostedZone, error) {
var PaginationControl *string
var hostedZones []route53types.HostedZone
cacheKey := fmt.Sprintf("%s-route53-ListHostedZones", accountID)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]route53types.HostedZone), nil
}
for {
ListHostedZones, err := client.ListHostedZones(
context.TODO(),
&route53.ListHostedZonesInput{
Marker: PaginationControl,
},
)
if err != nil {
return hostedZones, err
}
hostedZones = append(hostedZones, ListHostedZones.HostedZones...)
//pagination
if ListHostedZones.Marker == nil {
break
}
PaginationControl = ListHostedZones.Marker
}
internal.Cache.Set(cacheKey, hostedZones, cache.DefaultExpiration)
return hostedZones, nil
}
func CachedRoute53ListResourceRecordSets(client AWSRoute53ClientInterface, accountID string, hostedZoneID string) ([]route53types.ResourceRecordSet, error) {
var PaginationControl *string
var resourceRecordSets []route53types.ResourceRecordSet
// remove the /hostedzone/ prefix
hostedZoneID = hostedZoneID[12:]
cacheKey := fmt.Sprintf("%s-route53-ListResourceRecordSets-%s", accountID, hostedZoneID)
cached, found := internal.Cache.Get(cacheKey)
if found {
return cached.([]route53types.ResourceRecordSet), nil
}
for {
ListResourceRecordSets, err := client.ListResourceRecordSets(
context.TODO(),
&route53.ListResourceRecordSetsInput{
HostedZoneId: &hostedZoneID,
StartRecordName: PaginationControl,
},
)
if err != nil {
return resourceRecordSets, err
}
resourceRecordSets = append(resourceRecordSets, ListResourceRecordSets.ResourceRecordSets...)
//pagination
if !ListResourceRecordSets.IsTruncated {
break
}
PaginationControl = ListResourceRecordSets.NextRecordName
}
internal.Cache.Set(cacheKey, resourceRecordSets, cache.DefaultExpiration)
return resourceRecordSets, nil
}
+70
View File
@@ -0,0 +1,70 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/route53"
route53Types "github.com/aws/aws-sdk-go-v2/service/route53/types"
)
type MockedRoute53Client struct {
}
func (m *MockedRoute53Client) ListHostedZones(ctx context.Context, input *route53.ListHostedZonesInput, options ...func(*route53.Options)) (*route53.ListHostedZonesOutput, error) {
return &route53.ListHostedZonesOutput{
HostedZones: []route53Types.HostedZone{
{
Id: aws.String("/hostedzone/zone1"),
Name: aws.String("zone1"),
ResourceRecordSetCount: aws.Int64(3),
},
{
Id: aws.String("/hostedzone/zone2"),
Name: aws.String("zone2"),
ResourceRecordSetCount: aws.Int64(3),
},
},
}, nil
}
func (m *MockedRoute53Client) ListResourceRecordSets(ctx context.Context, input *route53.ListResourceRecordSetsInput, options ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error) {
return &route53.ListResourceRecordSetsOutput{
ResourceRecordSets: []route53Types.ResourceRecordSet{
{
Name: aws.String("zone1"),
Type: route53Types.RRTypeSoa,
},
{
Name: aws.String("zone1"),
Type: route53Types.RRTypeNs,
},
{
Name: aws.String("zone1"),
Type: route53Types.RRTypeA,
ResourceRecords: []route53Types.ResourceRecord{
{
Value: aws.String("unit-test"),
},
},
},
{
Name: aws.String("zone2"),
Type: route53Types.RRTypeSoa,
},
{
Name: aws.String("zone2"),
Type: route53Types.RRTypeNs,
},
{
Name: aws.String("zone2"),
Type: route53Types.RRTypeA,
ResourceRecords: []route53Types.ResourceRecord{
{
Value: aws.String("unit-test"),
},
},
},
},
}, nil
}
+1 -1
View File
@@ -19,7 +19,7 @@ type AWSS3ClientInterface interface {
GetPublicAccessBlock(ctx context.Context, params *s3.GetPublicAccessBlockInput, optFns ...func(*s3.Options)) (*s3.GetPublicAccessBlockOutput, error)
}
func RegisterS3Types() {
func init() {
gob.Register([]s3Types.Bucket{})
gob.Register(s3Types.Bucket{})
gob.Register(&s3Types.PublicAccessBlockConfiguration{})
+75
View File
@@ -0,0 +1,75 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
s3Types "github.com/aws/aws-sdk-go-v2/service/s3/types"
)
type MockedS3Client struct {
}
func (m *MockedS3Client) ListBuckets(ctx context.Context, input *s3.ListBucketsInput, options ...func(*s3.Options)) (*s3.ListBucketsOutput, error) {
return &s3.ListBucketsOutput{
Buckets: []s3Types.Bucket{
{
Name: aws.String("bucket1"),
},
{
Name: aws.String("bucket2"),
},
},
}, nil
}
func (m *MockedS3Client) GetBucketLocation(ctx context.Context, input *s3.GetBucketLocationInput, options ...func(*s3.Options)) (*s3.GetBucketLocationOutput, error) {
return &s3.GetBucketLocationOutput{
LocationConstraint: s3Types.BucketLocationConstraintUsWest1,
}, nil
}
func (m *MockedS3Client) GetBucketPolicy(ctx context.Context, input *s3.GetBucketPolicyInput, options ...func(*s3.Options)) (*s3.GetBucketPolicyOutput, error) {
return &s3.GetBucketPolicyOutput{
Policy: aws.String(`{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck20150319",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::bucket1"
},
{
"Sid": "AWSCloudTrailWrite20150319",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::bucket1/AWSLogs/123456789012/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}`),
}, nil
}
func (m *MockedS3Client) GetPublicAccessBlock(ctx context.Context, input *s3.GetPublicAccessBlockInput, options ...func(*s3.Options)) (*s3.GetPublicAccessBlockOutput, error) {
return &s3.GetPublicAccessBlockOutput{
PublicAccessBlockConfiguration: &s3Types.PublicAccessBlockConfiguration{
BlockPublicAcls: true,
BlockPublicPolicy: true,
IgnorePublicAcls: true,
RestrictPublicBuckets: true,
},
}, nil
}
+1 -1
View File
@@ -15,7 +15,7 @@ type SecretsManagerClientInterface interface {
ListSecrets(context.Context, *secretsmanager.ListSecretsInput, ...func(*secretsmanager.Options)) (*secretsmanager.ListSecretsOutput, error)
}
func RegisterSecretsManagerTypes() {
func init() {
gob.Register([]secretsmanagerTypes.SecretListEntry{})
}
+25
View File
@@ -0,0 +1,25 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
secretsmanagerTypes "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
)
type MockedSecretsManagerClient struct {
}
func (m *MockedSecretsManagerClient) ListSecrets(ctx context.Context, input *secretsmanager.ListSecretsInput, options ...func(*secretsmanager.Options)) (*secretsmanager.ListSecretsOutput, error) {
return &secretsmanager.ListSecretsOutput{
SecretList: []secretsmanagerTypes.SecretListEntry{
{
Name: aws.String("secret1"),
},
{
Name: aws.String("secret2"),
},
},
}, nil
}
+14 -1
View File
@@ -1,5 +1,18 @@
package sdk
import "github.com/BishopFox/cloudfox/internal"
import (
"log"
"os"
"github.com/BishopFox/cloudfox/internal"
)
var sharedLogger = internal.TxtLogger()
func readTestFile(testFile string) []byte {
file, err := os.ReadFile(testFile)
if err != nil {
log.Fatalf("can't read file %s", testFile)
}
return file
}
+181
View File
@@ -0,0 +1,181 @@
package sdk
import (
"context"
"encoding/gob"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/sns"
snsTypes "github.com/aws/aws-sdk-go-v2/service/sns/types"
"github.com/patrickmn/go-cache"
)
type AWSSNSClientInterface interface {
ListTopics(ctx context.Context, params *sns.ListTopicsInput, optFns ...func(*sns.Options)) (*sns.ListTopicsOutput, error)
ListSubscriptions(ctx context.Context, params *sns.ListSubscriptionsInput, optFns ...func(*sns.Options)) (*sns.ListSubscriptionsOutput, error)
ListSubscriptionsByTopic(ctx context.Context, params *sns.ListSubscriptionsByTopicInput, optFns ...func(*sns.Options)) (*sns.ListSubscriptionsByTopicOutput, error)
GetTopicAttributes(ctx context.Context, params *sns.GetTopicAttributesInput, optFns ...func(*sns.Options)) (*sns.GetTopicAttributesOutput, error)
}
func init() {
gob.Register([]string{})
gob.Register(snsTypes.Topic{})
gob.Register(snsTypes.Subscription{})
}
func CachedSNSListTopics(SNSClient AWSSNSClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var topics []string
cacheKey := "sns-ListTopics-" + accountID + "-" + region
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached SNS topics data")
return cached.([]string), nil
}
for {
ListTopics, err := SNSClient.ListTopics(
context.TODO(),
&sns.ListTopicsInput{
NextToken: PaginationControl,
},
func(o *sns.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
for _, topic := range ListTopics.Topics {
topics = append(topics, *topic.TopicArn)
}
// Pagination control.
if ListTopics.NextToken != nil {
PaginationControl = ListTopics.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, topics, cache.DefaultExpiration)
return topics, nil
}
func CachedSNSListSubscriptions(SNSClient AWSSNSClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var subscriptions []string
cacheKey := "sns-ListSubscriptions-" + accountID + "-" + region
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached SNS subscriptions data")
return cached.([]string), nil
}
for {
ListSubscriptions, err := SNSClient.ListSubscriptions(
context.TODO(),
&sns.ListSubscriptionsInput{
NextToken: PaginationControl,
},
func(o *sns.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
for _, subscription := range ListSubscriptions.Subscriptions {
subscriptions = append(subscriptions, *subscription.SubscriptionArn)
}
// Pagination control.
if ListSubscriptions.NextToken != nil {
PaginationControl = ListSubscriptions.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, subscriptions, cache.DefaultExpiration)
return subscriptions, nil
}
func CachedSNSListSubscriptionsByTopic(SNSClient AWSSNSClientInterface, accountID string, region string, topicArn string) ([]string, error) {
var PaginationControl *string
var subscriptions []string
cacheKey := "sns-ListSubscriptionsByTopic-" + accountID + "-" + region + "-" + topicArn
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached SNS subscriptions data")
return cached.([]string), nil
}
for {
ListSubscriptionsByTopic, err := SNSClient.ListSubscriptionsByTopic(
context.TODO(),
&sns.ListSubscriptionsByTopicInput{
NextToken: PaginationControl,
TopicArn: &topicArn,
},
func(o *sns.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
for _, subscription := range ListSubscriptionsByTopic.Subscriptions {
subscriptions = append(subscriptions, *subscription.SubscriptionArn)
}
// Pagination control.
if ListSubscriptionsByTopic.NextToken != nil {
PaginationControl = ListSubscriptionsByTopic.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, subscriptions, cache.DefaultExpiration)
return subscriptions, nil
}
func CachedSNSGetTopicAttributes(SNSClient AWSSNSClientInterface, accountID string, region string, topicArn string) (map[string]string, error) {
cacheKey := "sns-GetTopicAttributes-" + accountID + "-" + region + "-" + topicArn
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached SNS topic attributes data")
return cached.(map[string]string), nil
}
GetTopicAttributes, err := SNSClient.GetTopicAttributes(
context.TODO(),
&sns.GetTopicAttributesInput{
TopicArn: &topicArn,
},
func(o *sns.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
return nil, err
}
internal.Cache.Set(cacheKey, GetTopicAttributes.Attributes, cache.DefaultExpiration)
return GetTopicAttributes.Attributes, nil
}
+60
View File
@@ -0,0 +1,60 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/sns"
snsTypes "github.com/aws/aws-sdk-go-v2/service/sns/types"
)
type MockedSNSClient struct {
}
func (m *MockedSNSClient) ListTopics(ctx context.Context, input *sns.ListTopicsInput, options ...func(*sns.Options)) (*sns.ListTopicsOutput, error) {
return &sns.ListTopicsOutput{
Topics: []snsTypes.Topic{
{
TopicArn: aws.String("arn:aws:sns:us-east-1:123456789012:topic1"),
},
{
TopicArn: aws.String("arn:aws:sns:us-east-1:123456789012:topic2"),
},
},
}, nil
}
func (m *MockedSNSClient) ListSubscriptionsByTopic(ctx context.Context, input *sns.ListSubscriptionsByTopicInput, options ...func(*sns.Options)) (*sns.ListSubscriptionsByTopicOutput, error) {
return &sns.ListSubscriptionsByTopicOutput{
Subscriptions: []snsTypes.Subscription{
{
SubscriptionArn: aws.String("arn:aws:sns:us-east-1:123456789012:topic1:sub1"),
},
{
SubscriptionArn: aws.String("arn:aws:sns:us-east-1:123456789012:topic1:sub2"),
},
},
}, nil
}
func (m *MockedSNSClient) ListSubscriptions(ctx context.Context, input *sns.ListSubscriptionsInput, options ...func(*sns.Options)) (*sns.ListSubscriptionsOutput, error) {
return &sns.ListSubscriptionsOutput{
Subscriptions: []snsTypes.Subscription{
{
SubscriptionArn: aws.String("arn:aws:sns:us-east-1:123456789012:topic1:sub1"),
},
{
SubscriptionArn: aws.String("arn:aws:sns:us-east-1:123456789012:topic1:sub2"),
},
},
}, nil
}
func (m *MockedSNSClient) GetTopicAttributes(ctx context.Context, input *sns.GetTopicAttributesInput, options ...func(*sns.Options)) (*sns.GetTopicAttributesOutput, error) {
return &sns.GetTopicAttributesOutput{
Attributes: map[string]string{
"DisplayName": "topic1",
"Policy": `{"Statement":[{"Effect":"Allow","Principal":"*","Action":"SNS:Publish","Resource":"arn:aws:sns:us-east-1:123456789012:topoic1","Condition":{"StringEquals":{"aws:sourceVpce":"vpce-1a2b3c4d"}}}]}`,
},
}, nil
}
+58
View File
@@ -0,0 +1,58 @@
package sdk
import (
"context"
"encoding/gob"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/patrickmn/go-cache"
)
type AWSSQSClientInterface interface {
ListQueues(ctx context.Context, params *sqs.ListQueuesInput, optFns ...func(*sqs.Options)) (*sqs.ListQueuesOutput, error)
}
func init() {
gob.Register([]string{})
}
func CachedSQSListQueues(SQSClient AWSSQSClientInterface, accountID string, region string) ([]string, error) {
var PaginationControl *string
var queues []string
cacheKey := "sqs-ListQueues-" + accountID + "-" + region
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached SQS queues data")
return cached.([]string), nil
}
for {
ListQueues, err := SQSClient.ListQueues(
context.TODO(),
&sqs.ListQueuesInput{
NextToken: PaginationControl,
},
func(o *sqs.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
queues = append(queues, ListQueues.QueueUrls...)
// Pagination control.
if ListQueues.NextToken != nil {
PaginationControl = ListQueues.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, queues, cache.DefaultExpiration)
return queues, nil
}
+28
View File
@@ -0,0 +1,28 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/service/sqs"
)
type MockedSQSClient struct {
}
func (m *MockedSQSClient) ListQueues(ctx context.Context, input *sqs.ListQueuesInput, options ...func(*sqs.Options)) (*sqs.ListQueuesOutput, error) {
return &sqs.ListQueuesOutput{
QueueUrls: []string{
"https://sqs.us-east-1.amazonaws.com/123456789012/queue1",
"https://sqs.us-east-1.amazonaws.com/123456789012/queue2",
},
}, nil
}
func (m *MockedSQSClient) GetQueueAttributes(ctx context.Context, input *sqs.GetQueueAttributesInput, options ...func(*sqs.Options)) (*sqs.GetQueueAttributesOutput, error) {
return &sqs.GetQueueAttributesOutput{
Attributes: map[string]string{
"QueueArn": "arn:aws:sqs:us-east-1:123456789012:queue1",
"Policy": `{"Version": "2012-10-17","Id": "anyID","Statement": [{"Sid":"unconditionally_public","Effect": "Allow","Principal": {"AWS": "*"},"Action": "sqs:*","Resource": "arn:aws:sqs:*:123456789012:some-queue"}]}`,
},
}, nil
}
+61
View File
@@ -0,0 +1,61 @@
package sdk
import (
"context"
"encoding/gob"
"fmt"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/aws/aws-sdk-go-v2/service/ssm/types"
"github.com/patrickmn/go-cache"
)
type AWSSSMClientInterface interface {
DescribeParameters(ctx context.Context, params *ssm.DescribeParametersInput, optFns ...func(*ssm.Options)) (*ssm.DescribeParametersOutput, error)
}
func init() {
gob.Register([]types.ParameterMetadata{})
}
// create a CachedSSMDescribeParameters function that uses go-cache line the other Cached* functions. It should accept a ssm client, account id, and region. Make sure it handles the region option and pagination if needed
func CachedSSMDescribeParameters(SSMClient AWSSSMClientInterface, accountID string, region string) ([]types.ParameterMetadata, error) {
var PaginationControl *string
var parameters []types.ParameterMetadata
cacheKey := fmt.Sprintf("%s-ssm-DescribeParameters-%s", accountID, region)
cached, found := internal.Cache.Get(cacheKey)
if found {
sharedLogger.Debug("Using cached SSM parameters data")
return cached.([]types.ParameterMetadata), nil
}
for {
DescribeParameters, err := SSMClient.DescribeParameters(
context.TODO(),
&ssm.DescribeParametersInput{
NextToken: PaginationControl,
},
func(o *ssm.Options) {
o.Region = region
},
)
if err != nil {
sharedLogger.Error(err.Error())
break
}
parameters = append(parameters, DescribeParameters.Parameters...)
// Pagination control.
if DescribeParameters.NextToken != nil {
PaginationControl = DescribeParameters.NextToken
} else {
PaginationControl = nil
break
}
}
internal.Cache.Set(cacheKey, parameters, cache.DefaultExpiration)
return parameters, nil
}
+27
View File
@@ -0,0 +1,27 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ssm"
ssmTypes "github.com/aws/aws-sdk-go-v2/service/ssm/types"
)
type MockedSSMClient struct {
}
func (m *MockedSSMClient) DescribeParameters(ctx context.Context, input *ssm.DescribeParametersInput, options ...func(*ssm.Options)) (*ssm.DescribeParametersOutput, error) {
return &ssm.DescribeParametersOutput{
Parameters: []ssmTypes.ParameterMetadata{
{
Name: aws.String("/parameter/param1"),
Type: ssmTypes.ParameterTypeString,
},
{
Name: aws.String("/parameter/param2"),
Type: ssmTypes.ParameterTypeString,
},
},
}, nil
}
+1 -1
View File
@@ -15,7 +15,7 @@ type StepFunctionsClientInterface interface {
ListStateMachines(context.Context, *sfn.ListStateMachinesInput, ...func(*sfn.Options)) (*sfn.ListStateMachinesOutput, error)
}
func RegisterStepFunctionsTypes() {
func init() {
gob.Register([]sfnTypes.StateMachineListItem{})
}
+27
View File
@@ -0,0 +1,27 @@
package sdk
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/sfn"
sfnTypes "github.com/aws/aws-sdk-go-v2/service/sfn/types"
)
type MockedStepFunctionsClient struct {
}
func (m *MockedStepFunctionsClient) ListStateMachines(ctx context.Context, input *sfn.ListStateMachinesInput, options ...func(*sfn.Options)) (*sfn.ListStateMachinesOutput, error) {
return &sfn.ListStateMachinesOutput{
StateMachines: []sfnTypes.StateMachineListItem{
{
Name: aws.String("state_machine1"),
StateMachineArn: aws.String("arn:aws:states:us-east-1:123456789012:stateMachine:state_machine1"),
},
{
Name: aws.String("state_machine2"),
StateMachineArn: aws.String("arn:aws:states:us-east-1:123456789012:stateMachine:state_machine2"),
},
},
}, nil
}
+1 -1
View File
@@ -44,7 +44,7 @@ func TestSNSQueues(t *testing.T) {
// execute the module with verbosity = 2
m.PrintSNS("table", tmpDir, 2)
resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/123456789012-unittesting/table/sns.txt")
resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/unittesting-123456789012/table/sns.txt")
resultsFile, err := afero.ReadFile(fs, resultsFilePath)
if err != nil {
t.Fatalf("Cannot read output file at %s: %s", resultsFilePath, err)
+1 -1
View File
@@ -50,7 +50,7 @@ func TestSQSQueues(t *testing.T) {
// execute the module with verbosity set to 2
m.PrintSQS("table", tmpDir, 2)
resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/123456789012-unittesting/table/sqs.txt")
resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/unittesting-123456789012/table/sqs.txt")
resultsFile, err := afero.ReadFile(fs, resultsFilePath)
if err != nil {
t.Fatalf("Cannot read output file at %s: %s", resultsFilePath, err)
+141 -30
View File
@@ -14,7 +14,7 @@ import (
"github.com/kyokomi/emoji"
)
func AzInventoryCommand(AzTenantID, AzSubscriptionID, Version string, AzVerbosity int, AzWrapTable bool) error {
func AzInventoryCommand(AzTenantID, AzSubscriptionID, AzOutputDirectory, Version string, AzVerbosity int, AzWrapTable bool, AzMergedTable bool) error {
o := internal.OutputClient{
Verbosity: AzVerbosity,
CallingModule: globals.AZ_INVENTORY_MODULE_NAME,
@@ -24,36 +24,55 @@ func AzInventoryCommand(AzTenantID, AzSubscriptionID, Version string, AzVerbosit
}
if AzTenantID != "" && AzSubscriptionID == "" {
// To-Do: implement per tentant
fmt.Println("Inventory per tenant not yet implemented. Please use the --subscription flag instead.")
// cloudfox azure inventory --tenant [TENANT_ID | PRIMARY_DOMAIN]
tenantInfo := populateTenant(AzTenantID)
if AzMergedTable {
// set up table vars
var header []string
var body [][]string
o := internal.OutputClient{
Verbosity: AzVerbosity,
CallingModule: globals.AZ_INVENTORY_MODULE_NAME,
Table: internal.TableClient{
Wrap: AzWrapTable,
},
}
fmt.Printf(
"[%s][%s] Gathering inventory for subscription %s\n",
color.CyanString(emoji.Sprintf(":fox:cloudfox %s :fox:", Version)), color.CyanString(o.CallingModule),
fmt.Sprintf("%s (%s)", ptr.ToString(tenantInfo.DefaultDomain), ptr.ToString(tenantInfo.ID)))
o.PrefixIdentifier = ptr.ToString(tenantInfo.DefaultDomain)
o.Table.DirectoryName = filepath.Join(AzOutputDirectory, globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, ptr.ToString(tenantInfo.DefaultDomain), "1-tenant-level")
//populate the table data
header, body, err := getInventoryInfoPerTenant(ptr.ToString(tenantInfo.ID))
if err != nil {
return err
}
o.Table.TableFiles = append(o.Table.TableFiles,
internal.TableFile{
Header: header,
Body: body,
Name: fmt.Sprintf(o.CallingModule)})
if body != nil {
o.WriteFullOutput(o.Table.TableFiles, nil)
}
} else {
for _, s := range GetSubscriptionsPerTenantID(ptr.ToString(tenantInfo.ID)) {
runInventoryCommandForSingleSubscription(ptr.ToString(s.SubscriptionID), AzOutputDirectory, AzVerbosity, AzWrapTable, Version)
}
}
} else if AzTenantID == "" && AzSubscriptionID != "" {
// ./cloudfox azure storage --subscription SUBSCRIPTION_ID
fmt.Printf(
"[%s][%s] Gathering inventory for subscription %s\n",
color.CyanString(emoji.Sprintf(":fox:cloudfox %s :fox:", Version)),
color.CyanString(o.CallingModule),
AzSubscriptionID)
AzTenantID := ptr.ToString(GetTenantIDPerSubscription(AzSubscriptionID))
header, body, err := prepareInventoryTable(AzTenantID, AzSubscriptionID)
if err != nil {
return err
}
o.Table.TableFiles = append(o.Table.TableFiles,
internal.TableFile{
Header: header,
Body: body,
Name: globals.AZ_INVENTORY_MODULE_NAME})
o.PrefixIdentifier = fmt.Sprintf("subscription-%s", AzSubscriptionID)
o.Table.DirectoryName = filepath.Join(
globals.CLOUDFOX_BASE_DIRECTORY,
globals.AZ_DIR_BASE,
"subscriptions",
AzSubscriptionID)
// ./cloudfox azure inventory --subscription [SUBSCRIPTION_ID | SUBSCRIPTION_NAME]
runInventoryCommandForSingleSubscription(AzSubscriptionID, AzOutputDirectory, AzVerbosity, AzWrapTable, Version)
} else {
// Error: please make a valid flag selection
@@ -63,7 +82,51 @@ func AzInventoryCommand(AzTenantID, AzSubscriptionID, Version string, AzVerbosit
return nil
}
func prepareInventoryTable(tenantID, subscriptionID string) ([]string, [][]string, error) {
func runInventoryCommandForSingleSubscription(AzSubscription string, AzOutputDirectory string, AzVerbosity int, AzWrapTable bool, Version string) error {
// set up table vars
var header []string
var body [][]string
var err error
o := internal.OutputClient{
Verbosity: AzVerbosity,
CallingModule: globals.AZ_INVENTORY_MODULE_NAME,
Table: internal.TableClient{
Wrap: AzWrapTable,
},
}
var AzSubscriptionInfo SubsriptionInfo
tenantID := ptr.ToString(GetTenantIDPerSubscription(AzSubscription))
tenantInfo := populateTenant(tenantID)
AzSubscriptionInfo = PopulateSubsriptionType(AzSubscription)
o.PrefixIdentifier = AzSubscriptionInfo.Name
o.Table.DirectoryName = filepath.Join(AzOutputDirectory, globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, ptr.ToString(tenantInfo.DefaultDomain), AzSubscriptionInfo.Name)
fmt.Printf(
"[%s][%s] Gathering inventory for subscription %s\n",
color.CyanString(emoji.Sprintf(":fox:cloudfox %s :fox:", Version)), color.CyanString(o.CallingModule),
fmt.Sprintf("%s (%s)", AzSubscriptionInfo.Name, AzSubscriptionInfo.ID))
// populate the table data
header, body, err = getInventoryInfoPerSubscription(ptr.ToString(tenantInfo.ID), AzSubscriptionInfo.ID)
if err != nil {
return err
}
o.Table.TableFiles = append(o.Table.TableFiles,
internal.TableFile{
Header: header,
Body: body,
Name: fmt.Sprintf(globals.AZ_INVENTORY_MODULE_NAME)})
if body != nil {
o.WriteFullOutput(o.Table.TableFiles, nil)
fmt.Println()
}
return nil
}
func getInventoryInfoPerSubscription(tenantID, subscriptionID string) ([]string, [][]string, error) {
resources, err := getResources(tenantID, subscriptionID)
if err != nil {
return nil, nil, err
@@ -104,11 +167,59 @@ func prepareInventoryTable(tenantID, subscriptionID string) ([]string, [][]strin
}
body = append(body, row)
}
sort.Slice(body, func(i, j int) bool {
return body[i][0] < body[j][0]
})
return header, body, nil
}
func getInventoryInfoPerTenant(tenantID string) ([]string, [][]string, error) {
inventory := make(map[string]map[string]int)
resourceTypes := make(map[string]bool)
resourceLocations := make(map[string]bool)
for _, s := range GetSubscriptionsPerTenantID(tenantID) {
resources, err := getResources(tenantID, ptr.ToString(s.SubscriptionID))
if err != nil {
return nil, nil, err
}
for _, resource := range resources {
resourceType := ptr.ToString(resource.Type)
resourceLocation := ptr.ToString(resource.Location)
_, ok := inventory[resourceType]
if !ok {
inventory[resourceType] = make(map[string]int)
}
inventory[resourceType][resourceLocation]++
resourceTypes[resourceType] = true
resourceLocations[resourceLocation] = true
}
}
header := []string{"Resource Type"}
var body [][]string
for location := range resourceLocations {
header = append(header, location)
}
for t := range resourceTypes {
row := []string{t}
for location := range resourceLocations {
count, ok := inventory[t][location]
if ok {
row = append(row, fmt.Sprintf("%d", count))
} else {
row = append(row, "-")
}
}
body = append(body, row)
}
sort.Slice(body, func(i, j int) bool {
return body[i][0] < body[j][0]
})
return header, body, nil
}
+63 -21
View File
@@ -7,10 +7,11 @@ import (
"log"
"os"
"path/filepath"
"sort"
"strings"
"github.com/Azure/azure-sdk-for-go/profiles/latest/authorization/mgmt/authorization"
"github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac"
"github.com/Azure/azure-sdk-for-go/profiles/latest/graphrbac/graphrbac"
"github.com/BishopFox/cloudfox/globals"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/smithy-go/ptr"
@@ -18,38 +19,74 @@ import (
"github.com/kyokomi/emoji"
)
func AzRBACCommand(AzTenantID, AzSubscriptionID, AzOutputFormat, Version string, AzVerbosity int, AzWrapTable bool) error {
func AzRBACCommand(AzTenantID, AzSubscription, AzOutputFormat, AzOutputDirectory, Version string, AzVerbosity int, AzWrapTable bool, AzMergedTable bool) error {
// setup logging client
o := internal.OutputClient{
Verbosity: AzVerbosity,
CallingModule: globals.AZ_RBAC_MODULE_NAME,
Table: internal.TableClient{
Wrap: AzWrapTable,
},
}
// initiate command specific client
var c CloudFoxRBACclient
// set up table vars
var header []string
var body [][]string
var outputDirectory, controlMessagePrefix string
if AzTenantID != "" && AzSubscriptionID == "" {
// ./cloudfox azure rbac --tenant TENANT_ID
fmt.Printf("[%s][%s] Enumerating RBAC permissions for tenant %s\n", color.CyanString(emoji.Sprintf(":fox:cloudfox %s :fox:", Version)), color.CyanString(globals.AZ_RBAC_MODULE_NAME), AzTenantID)
controlMessagePrefix = fmt.Sprintf("tenant-%s", AzTenantID)
outputDirectory = filepath.Join(globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, "tenants", AzTenantID)
var AzSubscriptionInfo SubsriptionInfo
if AzTenantID != "" && AzSubscription == "" {
// cloudfox azure rbac --tenant [TENANT_ID | PRIMARY_DOMAIN]
var err error
header, body, err = getRBACperTenant(AzTenantID, c)
tenantInfo := populateTenant(AzTenantID)
if err != nil {
return err
}
} else if AzTenantID == "" && AzSubscriptionID != "" {
// ./cloudfox azure rbac --subscription SUBSCRIPTION_ID
fmt.Printf("[%s][%s] Enumerating RBAC permissions for subscription %s\n", color.CyanString(emoji.Sprintf(":fox:cloudfox %s :fox:", Version)), color.CyanString(globals.AZ_RBAC_MODULE_NAME), AzSubscriptionID)
controlMessagePrefix = fmt.Sprintf("subscription-%s", AzSubscriptionID)
outputDirectory = filepath.Join(globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, "subscriptions", AzSubscriptionID)
header, body = getRBACperSubscription(AzTenantID, AzSubscriptionID, c)
o.PrefixIdentifier = ptr.ToString(tenantInfo.DefaultDomain)
o.Table.DirectoryName = filepath.Join(AzOutputDirectory, globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, ptr.ToString(tenantInfo.DefaultDomain), "1-tenant-level")
fmt.Printf("[%s][%s] Enumerating RBAC permissions for tenant %s\n",
color.CyanString(emoji.Sprintf(":fox:cloudfox %s :fox:", Version)), color.CyanString(globals.AZ_RBAC_MODULE_NAME),
fmt.Sprintf("%s (%s)", ptr.ToString(tenantInfo.DefaultDomain), ptr.ToString(tenantInfo.ID)))
header, body, err = getRBACperTenant(ptr.ToString(tenantInfo.ID), c)
if err != nil {
return err
}
o.Table.TableFiles = append(o.Table.TableFiles,
internal.TableFile{
Header: header,
Body: body,
Name: fmt.Sprintf(globals.AZ_RBAC_MODULE_NAME)})
} else if AzTenantID == "" && AzSubscription != "" {
// cloudfox azure rbac --subscription [SUBSCRIPTION_ID | SUBSCRIPTION_NAME]
tenantID := ptr.ToString(GetTenantIDPerSubscription(AzSubscription))
tenantInfo := populateTenant(tenantID)
AzSubscriptionInfo = PopulateSubsriptionType(AzSubscription)
o.PrefixIdentifier = AzSubscriptionInfo.Name
o.Table.DirectoryName = filepath.Join(AzOutputDirectory, globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, ptr.ToString(tenantInfo.DefaultDomain), AzSubscriptionInfo.Name)
fmt.Printf("[%s][%s] Enumerating RBAC permissions for subscription %s\n", color.CyanString(emoji.Sprintf(":fox:cloudfox %s :fox:", Version)), color.CyanString(globals.AZ_RBAC_MODULE_NAME),
fmt.Sprintf("%s (%s)", AzSubscriptionInfo.Name, AzSubscriptionInfo.ID))
header, body = getRBACperSubscription(ptr.ToString(tenantInfo.ID), AzSubscriptionInfo.ID, c)
o.Table.TableFiles = append(o.Table.TableFiles,
internal.TableFile{
Header: header,
Body: body,
Name: fmt.Sprintf(globals.AZ_RBAC_MODULE_NAME)})
} else {
// Error: please make a valid flag selection
fmt.Println("Please enter a valid input with a valid flag. Use --help for info.")
}
fileNameWithoutExtension := globals.AZ_RBAC_MODULE_NAME
if body != nil {
internal.OutputSelector(AzVerbosity, AzOutputFormat, header, body, outputDirectory, fileNameWithoutExtension, globals.AZ_RBAC_MODULE_NAME, AzWrapTable, controlMessagePrefix)
//internal.OutputSelector(AzVerbosity, AzOutputFormat, header, body, outputDirectory, fileNameWithoutExtension, globals.AZ_RBAC_MODULE_NAME, AzWrapTable, controlMessagePrefix)
o.WriteFullOutput(o.Table.TableFiles, nil)
}
return nil
}
@@ -57,7 +94,7 @@ func AzRBACCommand(AzTenantID, AzSubscriptionID, AzOutputFormat, Version string,
func getRBACperTenant(AzTenantID string, c CloudFoxRBACclient) ([]string, [][]string, error) {
var selectedSubs, resultsHeader []string
var resultsBody, b [][]string
for _, s := range getSubscriptions() {
for _, s := range GetSubscriptions() {
if ptr.ToString(s.TenantID) == AzTenantID {
selectedSubs = append(selectedSubs, ptr.ToString(s.SubscriptionID))
}
@@ -76,7 +113,7 @@ func getRBACperTenant(AzTenantID string, c CloudFoxRBACclient) ([]string, [][]st
func getRBACperSubscription(AzTenantID, AzSubscriptionID string, c CloudFoxRBACclient) ([]string, [][]string) {
var resultsHeader []string
var resultsBody [][]string
for _, s := range getSubscriptions() {
for _, s := range GetSubscriptions() {
if ptr.ToString(s.SubscriptionID) == AzSubscriptionID {
c.initialize(AzTenantID, []string{ptr.ToString(s.SubscriptionID)})
resultsHeader, resultsBody = c.GetRelevantRBACData(AzTenantID, ptr.ToString(s.SubscriptionID))
@@ -132,8 +169,13 @@ func (c *CloudFoxRBACclient) GetRelevantRBACData(tenantID, subscriptionID string
findRole(c.roleDefinitions, rb, &roleAssignmentRelevantData)
results = append(results, roleAssignmentRelevantData)
}
// Sort the results by userDisplayName using slice.Sort
sortedResults := results
sort.Slice(sortedResults, func(i, j int) bool {
return sortedResults[i].userDisplayName < sortedResults[j].userDisplayName
})
for _, r := range results {
for _, r := range sortedResults {
body = append(body,
[]string{
r.userDisplayName,
+10 -2
View File
@@ -20,18 +20,21 @@ func TestAzRBACCommand(t *testing.T) {
azRGName string
azVerbosity int
azOutputFormat string
azOutputDirectory string
version string
resourcesTestFile string
usersTestFile string
roleDefinitionsTestFile string
roleAssignmentsTestFile string
wrapTableOutput bool
azMergedTable bool
}{
{
name: "./cloudfox azure rbac --tenant 11111111-1111-1111-1111-11111111",
azTenantID: "11111111-1111-1111-1111-11111111",
azSubscriptionID: "",
azOutputFormat: "all",
azOutputDirectory: "~/.cloudfox",
azVerbosity: 2,
resourcesTestFile: "./test-data/resources.json",
usersTestFile: "./test-data/users.json",
@@ -39,12 +42,14 @@ func TestAzRBACCommand(t *testing.T) {
roleAssignmentsTestFile: "./test-data/role-assignments.json",
version: "DEV",
wrapTableOutput: false,
azMergedTable: false,
},
{
name: "./cloudfox azure rbac --subscription AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAA",
azTenantID: "",
azSubscriptionID: "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAA",
azOutputFormat: "all",
azOutputDirectory: "~/.cloudfox",
azVerbosity: 2,
version: "DEV",
resourcesTestFile: "./test-data/resources.json",
@@ -52,22 +57,25 @@ func TestAzRBACCommand(t *testing.T) {
roleDefinitionsTestFile: "./test-data/role-definitions.json",
roleAssignmentsTestFile: "./test-data/role-assignments.json",
wrapTableOutput: false,
azMergedTable: false,
},
{
name: "./cloudfox azure rbac",
azOutputFormat: "all",
azVerbosity: 2,
azOutputDirectory: "~/.cloudfox",
version: "DEV",
resourcesTestFile: "./test-data/resources.json",
usersTestFile: "./test-data/users.json",
roleDefinitionsTestFile: "./test-data/role-definitions.json",
roleAssignmentsTestFile: "./test-data/role-assignments.json",
wrapTableOutput: false,
azMergedTable: false,
},
}
internal.MockFileSystem(true)
// Mocked functions to simulate Azure calls and responses
getSubscriptions = mockedGetSubscriptions
GetSubscriptions = mockedGetSubscriptions
getAzureADUsers = mockedGetAzureADUsers
getRoleDefinitions = mockedGetRoleDefinitions
getRoleAssignments = mockedGetRoleAssignments
@@ -82,7 +90,7 @@ func TestAzRBACCommand(t *testing.T) {
globals.ROLE_DEFINITIONS_TEST_FILE = s.roleDefinitionsTestFile
globals.ROLE_ASSIGNMENTS_TEST_FILE = s.roleAssignmentsTestFile
if err := AzRBACCommand(s.azTenantID, s.azSubscriptionID, s.azOutputFormat, s.version, s.azVerbosity, s.wrapTableOutput); err != nil {
if err := AzRBACCommand(s.azTenantID, s.azSubscriptionID, s.azOutputFormat, s.azOutputDirectory, s.version, 2, s.wrapTableOutput, s.azMergedTable); err != nil {
fmt.Println(err)
}
}
+129
View File
@@ -0,0 +1,129 @@
package azure
import (
"context"
"fmt"
"github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/subscriptions"
"github.com/BishopFox/cloudfox/internal"
"github.com/aws/smithy-go/ptr"
)
type TenantInfo struct {
ID *string
DefaultDomain *string
Subscriptions []SubsriptionInfo
}
type SubsriptionInfo struct {
Subscription subscriptions.Subscription
ID string
Name string
}
// function that takes a subscription ID and returns the DisplayName of the subscription
func GetSubscriptionNameFromID(subscriptionID string) *string {
subs := GetSubscriptions()
for _, s := range subs {
if ptr.ToString(s.SubscriptionID) == subscriptionID {
return s.DisplayName
}
}
return nil
}
func GetSubscriptionIDFromName(subscriptionName string) *string {
subs := GetSubscriptions()
for _, s := range subs {
if ptr.ToString(s.DisplayName) == subscriptionName {
return s.SubscriptionID
}
}
return nil
}
// function that takes the AzSubscription string and first checks to see if it is a valid subscription ID, and if not, checks to see if it is a valid subscription display name. It then returns the subscription ID
func GetSubscriptionID(subscription string) *string {
subs := GetSubscriptions()
for _, s := range subs {
if ptr.ToString(s.SubscriptionID) == subscription {
return s.SubscriptionID
}
if ptr.ToString(s.DisplayName) == subscription {
return s.SubscriptionID
}
}
return nil
}
func GetSubscriptionsPerTenantID(tenantID string) []subscriptions.Subscription {
subs := GetSubscriptions()
var results []subscriptions.Subscription
for _, s := range subs {
if ptr.ToString(s.TenantID) == tenantID {
results = append(results, s)
}
}
return results
}
func GetTenantIDPerSubscription(subscriptionID string) *string {
subs := GetSubscriptions()
for _, s := range subs {
if ptr.ToString(s.SubscriptionID) == subscriptionID {
return s.TenantID
}
if ptr.ToString(s.DisplayName) == subscriptionID {
return s.TenantID
}
}
return nil
}
// function that determins if AzSubsriptionType is a subscritpion ID or a subscription display name and returns the AzSubsriptionType struct with both populated
func PopulateSubsriptionType(subscription string) SubsriptionInfo {
subs := GetSubscriptions()
for _, s := range subs {
if ptr.ToString(s.SubscriptionID) == subscription {
return SubsriptionInfo{ID: subscription, Name: ptr.ToString(s.DisplayName)}
}
if ptr.ToString(s.DisplayName) == subscription {
return SubsriptionInfo{ID: ptr.ToString(s.SubscriptionID), Name: subscription}
}
}
return SubsriptionInfo{}
}
func GetDefaultDomainFromTenantID(tenantID string) (string, error) {
// Get the client using the function
client := internal.GetgraphRbacClient(tenantID)
// List domains
domainList, err := client.List(context.Background(), "")
if err != nil {
return "", err
}
for _, domain := range *domainList.Value {
if *domain.IsDefault {
primaryDomain := *domain.Name
return primaryDomain, nil
}
}
return "", fmt.Errorf("No default domain found")
}
func populateTenant(tenantID string) TenantInfo {
for _, t := range getTenants() {
if ptr.ToString(t.TenantID) == tenantID || ptr.ToString(t.DefaultDomain) == tenantID {
var subscriptions []SubsriptionInfo
for _, s := range GetSubscriptionsPerTenantID(ptr.ToString(t.ID)) {
subscriptions = append(subscriptions, SubsriptionInfo{Subscription: s, ID: ptr.ToString(s.SubscriptionID), Name: ptr.ToString(s.DisplayName)})
}
return TenantInfo{ID: t.TenantID, DefaultDomain: t.DefaultDomain, Subscriptions: subscriptions}
}
}
return TenantInfo{}
}
Loaded 100 of 116 files, more files were not shown because too many files have changed in this diff. Show more