aws_recon 0.2.25 → 0.2.30

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -1
  3. data/lib/aws_recon/collectors/accessanalyzer.rb +5 -0
  4. data/lib/aws_recon/collectors/acm.rb +5 -0
  5. data/lib/aws_recon/collectors/apigateway.rb +5 -0
  6. data/lib/aws_recon/collectors/apigatewayv2.rb +5 -0
  7. data/lib/aws_recon/collectors/applicationautoscaling.rb +5 -0
  8. data/lib/aws_recon/collectors/athena.rb +5 -0
  9. data/lib/aws_recon/collectors/autoscaling.rb +5 -0
  10. data/lib/aws_recon/collectors/backup.rb +5 -0
  11. data/lib/aws_recon/collectors/cloudformation.rb +5 -0
  12. data/lib/aws_recon/collectors/cloudfront.rb +5 -0
  13. data/lib/aws_recon/collectors/cloudtrail.rb +6 -1
  14. data/lib/aws_recon/collectors/cloudwatch.rb +6 -0
  15. data/lib/aws_recon/collectors/cloudwatchlogs.rb +5 -0
  16. data/lib/aws_recon/collectors/codebuild.rb +5 -0
  17. data/lib/aws_recon/collectors/codepipeline.rb +6 -3
  18. data/lib/aws_recon/collectors/configservice.rb +5 -0
  19. data/lib/aws_recon/collectors/directconnect.rb +5 -0
  20. data/lib/aws_recon/collectors/{directyservice.rb → directoryservice.rb} +5 -0
  21. data/lib/aws_recon/collectors/dms.rb +5 -0
  22. data/lib/aws_recon/collectors/dynamodb.rb +5 -0
  23. data/lib/aws_recon/collectors/ec2.rb +6 -3
  24. data/lib/aws_recon/collectors/ecr.rb +6 -3
  25. data/lib/aws_recon/collectors/ecs.rb +12 -9
  26. data/lib/aws_recon/collectors/efs.rb +5 -0
  27. data/lib/aws_recon/collectors/eks.rb +5 -0
  28. data/lib/aws_recon/collectors/elasticache.rb +5 -0
  29. data/lib/aws_recon/collectors/elasticloadbalancing.rb +5 -0
  30. data/lib/aws_recon/collectors/elasticloadbalancingv2.rb +5 -0
  31. data/lib/aws_recon/collectors/elasticsearch.rb +6 -0
  32. data/lib/aws_recon/collectors/emr.rb +5 -0
  33. data/lib/aws_recon/collectors/firehose.rb +5 -0
  34. data/lib/aws_recon/collectors/guardduty.rb +5 -2
  35. data/lib/aws_recon/collectors/iam.rb +7 -6
  36. data/lib/aws_recon/collectors/kafka.rb +5 -0
  37. data/lib/aws_recon/collectors/kinesis.rb +5 -0
  38. data/lib/aws_recon/collectors/kms.rb +7 -4
  39. data/lib/aws_recon/collectors/lambda.rb +5 -0
  40. data/lib/aws_recon/collectors/lightsail.rb +5 -0
  41. data/lib/aws_recon/collectors/organizations.rb +6 -3
  42. data/lib/aws_recon/collectors/rds.rb +5 -0
  43. data/lib/aws_recon/collectors/redshift.rb +5 -0
  44. data/lib/aws_recon/collectors/route53.rb +5 -0
  45. data/lib/aws_recon/collectors/route53domains.rb +5 -0
  46. data/lib/aws_recon/collectors/s3.rb +6 -3
  47. data/lib/aws_recon/collectors/sagemaker.rb +25 -1
  48. data/lib/aws_recon/collectors/secretsmanager.rb +5 -0
  49. data/lib/aws_recon/collectors/securityhub.rb +6 -3
  50. data/lib/aws_recon/collectors/servicequotas.rb +6 -3
  51. data/lib/aws_recon/collectors/ses.rb +5 -0
  52. data/lib/aws_recon/collectors/shield.rb +7 -4
  53. data/lib/aws_recon/collectors/sns.rb +5 -0
  54. data/lib/aws_recon/collectors/sqs.rb +6 -1
  55. data/lib/aws_recon/collectors/ssm.rb +5 -0
  56. data/lib/aws_recon/collectors/support.rb +6 -3
  57. data/lib/aws_recon/collectors/transfer.rb +5 -0
  58. data/lib/aws_recon/collectors/wafv2.rb +5 -0
  59. data/lib/aws_recon/collectors/workspaces.rb +5 -0
  60. data/lib/aws_recon/collectors/xray.rb +5 -0
  61. data/lib/aws_recon/lib/mapper.rb +8 -6
  62. data/lib/aws_recon/lib/patch.rb +2 -0
  63. data/lib/aws_recon/options.rb +5 -6
  64. data/lib/aws_recon/version.rb +1 -1
  65. data/readme.md +72 -39
  66. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8786af60d08967c2203b1f4417480b135ae32b9a40ec05810afc9c2ae126f49d
4
- data.tar.gz: fbd4741002a54dc15f2b2716dee6e1b6efcec48edfdf3e101d9c7bcb19105a4e
3
+ metadata.gz: 818784fdc3d39b16bc30cafd043aec9396f3a8bb0e1fb62b696deb40a861d953
4
+ data.tar.gz: ebd7c961414a16e7cfeb60e8f3f871051bfd0705cc7a5408cc8fdd054d5f7925
5
5
  SHA512:
6
- metadata.gz: 2c11e60d61cf9fcf894b8e38cb136e934b03fd7e9ef1bd349ce27c41c9943e75cb146893d14374167d7f2eb73b40d97a901fbaebd6e6aee5ac93ef8095b1d0b4
7
- data.tar.gz: 9510a49029fbc382fefc50ad8ae0de0351b6f8648a1044abbebdf695459ef5a4845014057618387549546876c10ea1eb09cba55f4692a0405f41b96810c0dce5
6
+ metadata.gz: 52fc427274d6ac70c8565eb139681a3c0e0b243db9ee1acaced741fcc688496ce383629755d6e3aa49ec1bfdf47891adce77b88a2a095f000245461ea2508f0d
7
+ data.tar.gz: e6f4fe0b53ea000b766c94f8eaafa7dddf33086916419ae14fd65db7218ecb853d85d94e546c1be086ec48d4837b5fa64708326f7c2e8517a04602ff7b4dd9e7
@@ -9,4 +9,20 @@
9
9
  #
10
10
  # See https://docs.rubocop.org/rubocop/configuration
11
11
  Layout/LineLength:
12
- Max: 80
12
+ Max: 100
13
+ Style/FrozenStringLiteralComment:
14
+ EnforcedStyle: always_true
15
+ Safe: true
16
+ SafeAutoCorrect: true
17
+ Style/ClassAndModuleChildren:
18
+ Enabled: false
19
+ Metrics/BlockLength:
20
+ Enabled: false
21
+ Metrics/MethodLength:
22
+ Enabled: false
23
+ Metrics/PerceivedComplexity:
24
+ Enabled: false
25
+ Metrics/CyclomaticComplexity:
26
+ Enabled: false
27
+ Metrics/AbcSize:
28
+ Enabled: false
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect AccessAnalyzer resources
5
+ #
1
6
  class AccessAnalyzer < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect ACM resources
5
+ #
1
6
  class ACM < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect API Gateway resources
5
+ #
1
6
  class APIGateway < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect API Gateway v2 resources
5
+ #
1
6
  class ApiGatewayV2 < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect ApplicationAutoScaling resources
5
+ #
1
6
  class ApplicationAutoScaling < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Athena resources
5
+ #
1
6
  class Athena < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect AutoScaling resources
5
+ #
1
6
  class AutoScaling < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Backup resources
5
+ #
1
6
  class Backup < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect CloudFormation resources
5
+ #
1
6
  class CloudFormation < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect CloudFront resources
5
+ #
1
6
  class CloudFront < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect CloudTrail resources
5
+ #
1
6
  class CloudTrail < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -19,7 +24,7 @@ class CloudTrail < Mapper
19
24
  end
20
25
 
21
26
  struct = OpenStruct.new(trail.to_h)
22
- struct.tags = client.list_tags({ resource_id_list: [trail.trail_arn] }).resource_tag_list.first.tags_list
27
+ struct.tags = client.list_tags({ resource_id_list: [trail.trail_arn] }).resource_tag_list.first.tags_list.map(&:to_h)
23
28
  struct.type = 'cloud_trail'
24
29
  struct.event_selectors = client.get_event_selectors({ trail_name: trail.name }).to_h
25
30
  struct.status = client.get_trail_status({ name: trail.name }).to_h
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect CloudWatch resources
5
+ #
1
6
  class CloudWatch < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -23,6 +28,7 @@ class CloudWatch < Mapper
23
28
  struct = OpenStruct.new(alarm.to_h)
24
29
  struct.type = 'metric_alarm'
25
30
  struct.arn = alarm.alarm_arn
31
+ struct.state_reason_data = alarm.state_reason_data&.parse_policy
26
32
 
27
33
  resources.push(struct.to_h)
28
34
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect CloudWatchLogs resources
5
+ #
1
6
  class CloudWatchLogs < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect CodeBuild resources
5
+ #
1
6
  class CodeBuild < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect CodePipeline resources
5
+ #
1
6
  class CodePipeline < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -25,9 +30,7 @@ class CodePipeline < Mapper
25
30
  rescue Aws::CodePipeline::Errors::ServiceError => e
26
31
  log_error(e.code)
27
32
 
28
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
29
- raise e
30
- end
33
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
31
34
  end
32
35
 
33
36
  resources
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Config resources
5
+ #
1
6
  class ConfigService < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect DirectConnect resources
5
+ #
1
6
  class DirectConnect < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect DirectoryService resources
5
+ #
1
6
  class DirectoryService < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect DMS resources
5
+ #
1
6
  class DatabaseMigrationService < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect DynamodDB resources
5
+ #
1
6
  class DynamoDB < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect EC2 resources
5
+ #
1
6
  class EC2 < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -71,9 +76,7 @@ class EC2 < Mapper
71
76
  if user_data_raw
72
77
  user_data = Base64.decode64(user_data_raw)
73
78
 
74
- if user_data.force_encoding('UTF-8').ascii_only?
75
- struct.user_data = user_data
76
- end
79
+ struct.user_data = user_data if user_data.force_encoding('UTF-8').ascii_only?
77
80
  end
78
81
  end
79
82
 
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect ECR resources
5
+ #
1
6
  class ECR < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -21,9 +26,7 @@ class ECR < Mapper
21
26
  rescue Aws::ECR::Errors::ServiceError => e
22
27
  log_error(e.code)
23
28
 
24
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
25
- raise e
26
- end
29
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
27
30
  ensure
28
31
  resources.push(struct.to_h)
29
32
  end
@@ -1,31 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect ECS resources
5
+ #
1
6
  class ECS < Mapper
2
7
  #
3
8
  # Returns an array of resources.
4
9
  #
5
- # TODO: test live
6
- #
7
10
  def collect
8
11
  resources = []
9
12
 
10
13
  #
11
- # describe_clusters
14
+ # list_clusters
12
15
  #
13
- @client.describe_clusters.each_with_index do |response, page|
16
+ @client.list_clusters.each_with_index do |response, page|
14
17
  log(response.context.operation_name, page)
15
18
 
16
- response.clusters.each do |cluster|
17
- struct = OpenStruct.new(cluster.to_h)
19
+ response.cluster_arns.each do |cluster|
20
+ struct = OpenStruct.new(@client.describe_clusters({ clusters: [cluster] }).clusters.first.to_h)
18
21
  struct.type = 'cluster'
19
- struct.arn = cluster.cluster_arn
22
+ struct.arn = cluster
20
23
  struct.tasks = []
21
24
 
22
25
  # list_tasks
23
- @client.list_tasks({ cluster: cluster.cluster_arn }).each_with_index do |response, page|
26
+ @client.list_tasks({ cluster: cluster }).each_with_index do |response, page|
24
27
  log(response.context.operation_name, 'list_tasks', page)
25
28
 
26
29
  # describe_tasks
27
30
  response.task_arns.each do |task_arn|
28
- @client.describe_tasks({ cluster: cluster.cluster_arn, tasks: [task_arn] }).tasks.each do |task|
31
+ @client.describe_tasks({ cluster: cluster, tasks: [task_arn] }).tasks.each do |task|
29
32
  struct.tasks.push(task)
30
33
  end
31
34
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect EFS resources
5
+ #
1
6
  class EFS < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect EKS resources
5
+ #
1
6
  class EKS < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect ElastiCache resources
5
+ #
1
6
  class ElastiCache < Mapper
2
7
  def collect
3
8
  resources = []
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect ELB resources
5
+ #
1
6
  class ElasticLoadBalancing < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect ELBv2 resources
5
+ #
1
6
  class ElasticLoadBalancingV2 < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect ElasticSearch resources
5
+ #
1
6
  class ElasticsearchService < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -17,6 +22,7 @@ class ElasticsearchService < Mapper
17
22
  # describe_elasticsearch_domains
18
23
  struct = OpenStruct.new(@client.describe_elasticsearch_domain({ domain_name: domain.domain_name }).domain_status.to_h)
19
24
  struct.type = 'domain'
25
+ struct.access_policies = struct.access_policies&.parse_policy
20
26
 
21
27
  resources.push(struct.to_h)
22
28
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect EMR resources
5
+ #
1
6
  class EMR < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Firehose resources
5
+ #
1
6
  class Firehose < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect GuardDuty resources
5
+ #
1
6
  class GuardDuty < Mapper
2
7
  #
3
8
  # Returns an array of resources.
4
9
  #
5
- # TODO: test live
6
- #
7
10
  def collect
8
11
  resources = []
9
12
 
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect IAM resources
5
+ #
1
6
  class IAM < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -102,9 +107,7 @@ class IAM < Mapper
102
107
  rescue Aws::IAM::Errors::ServiceError => e
103
108
  log_error(e.code)
104
109
 
105
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
106
- raise e
107
- end
110
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
108
111
  end
109
112
 
110
113
  #
@@ -187,9 +190,7 @@ class IAM < Mapper
187
190
  rescue Aws::IAM::Errors::ServiceError => e
188
191
  log_error(e.code)
189
192
 
190
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
191
- raise e
192
- end
193
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
193
194
  end
194
195
 
195
196
  resources
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Kafka resources
5
+ #
1
6
  class Kafka < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Kinesis resources
5
+ #
1
6
  class Kinesis < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect KMS resources
5
+ #
1
6
  class KMS < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -30,9 +35,7 @@ class KMS < Mapper
30
35
  rescue Aws::KMS::Errors::ServiceError => e
31
36
  log_error(e.code)
32
37
 
33
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
34
- raise e
35
- end
38
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
36
39
  end
37
40
 
38
41
  # list_grants
@@ -48,7 +51,7 @@ class KMS < Mapper
48
51
  log(response.context.operation_name, 'get_key_policy')
49
52
  struct.policy = @client
50
53
  .get_key_policy({ key_id: key.key_id, policy_name: 'default' })
51
- .policy
54
+ .policy.parse_policy
52
55
 
53
56
  # list_aliases
54
57
  log(response.context.operation_name, 'list_aliases')
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Lambda resources
5
+ #
1
6
  class Lambda < Mapper
2
7
  def collect
3
8
  resources = []
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Lightsail resources
5
+ #
1
6
  class Lightsail < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Org resources
5
+ #
1
6
  class Organizations < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -49,9 +54,7 @@ class Organizations < Mapper
49
54
  rescue Aws::Organizations::Errors::ServiceError => e
50
55
  log_error(e.code)
51
56
 
52
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
53
- raise e
54
- end
57
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
55
58
  end
56
59
 
57
60
  resources
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect RDS Resources
5
+ #
1
6
  class RDS < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Redshift resources
5
+ #
1
6
  class Redshift < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Route53 resources
5
+ #
1
6
  class Route53 < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Route53 Domain resources
5
+ #
1
6
  class Route53Domains < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect S3 Resources
5
+ #
1
6
  class S3 < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -63,9 +68,7 @@ class S3 < Mapper
63
68
  rescue Aws::S3::Errors::ServiceError => e
64
69
  log_error(e.code)
65
70
 
66
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
67
- raise e
68
- end
71
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
69
72
  end
70
73
 
71
74
  resources.push(struct.to_h)
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect SageMaker Resources
5
+ #
1
6
  class SageMaker < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -12,7 +17,9 @@ class SageMaker < Mapper
12
17
  log(response.context.operation_name, page)
13
18
 
14
19
  response.notebook_instances.each do |instance|
15
- struct = OpenStruct.new(instance.to_h)
20
+ struct = OpenStruct.new(@client.describe_notebook_instance({
21
+ notebook_instance_name: instance.notebook_instance_name
22
+ }).to_h)
16
23
  struct.type = 'notebook_instance'
17
24
  struct.arn = instance.notebook_instance_arn
18
25
 
@@ -20,6 +27,23 @@ class SageMaker < Mapper
20
27
  end
21
28
  end
22
29
 
30
+ #
31
+ # list_endpoints
32
+ #
33
+ @client.list_endpoints.each_with_index do |response, page|
34
+ log(response.context.operation_name, page)
35
+
36
+ response.endpoints.each do |instance|
37
+ struct = OpenStruct.new(@client.describe_endpoint({
38
+ endpoint_name: instance.endpoint_name
39
+ }).to_h)
40
+ struct.type = 'endpoint'
41
+ struct.arn = instance.endpoint_arn
42
+
43
+ resources.push(struct.to_h)
44
+ end
45
+ end
46
+
23
47
  resources
24
48
  end
25
49
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Secrets Manager resources
5
+ #
1
6
  class SecretsManager < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Security Hub resources
5
+ #
1
6
  class SecurityHub < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -21,9 +26,7 @@ class SecurityHub < Mapper
21
26
  rescue Aws::SecurityHub::Errors::ServiceError => e
22
27
  log_error(e.code)
23
28
 
24
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
25
- raise e
26
- end
29
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
27
30
  end
28
31
 
29
32
  resources
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect ServiceQuota resources
5
+ #
1
6
  class ServiceQuotas < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -28,9 +33,7 @@ class ServiceQuotas < Mapper
28
33
  rescue Aws::ServiceQuotas::Errors::ServiceError => e
29
34
  log_error(e.code, service)
30
35
 
31
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
32
- raise e
33
- end
36
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
34
37
  end
35
38
 
36
39
  resources
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect SES resources
5
+ #
1
6
  class SES < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Shield resources
5
+ #
1
6
  class Shield < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -27,7 +32,7 @@ class Shield < Mapper
27
32
  struct = OpenStruct.new
28
33
  struct.type = 'contact_list'
29
34
  struct.arn = "arn:aws:shield:#{@region}:#{@account}:contact_list"
30
- struct.contacts = response.emergency_contact_list.map(&:to_h)
35
+ struct.contacts = response&.emergency_contact_list&.map(&:to_h)
31
36
 
32
37
  resources.push(struct.to_h)
33
38
  end
@@ -52,9 +57,7 @@ class Shield < Mapper
52
57
  rescue Aws::Shield::Errors::ServiceError => e
53
58
  log_error(e.code)
54
59
 
55
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
56
- raise e
57
- end
60
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
58
61
 
59
62
  [] # no access or service isn't enabled
60
63
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect SNS resources
5
+ #
1
6
  class SNS < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect SQS resources
5
+ #
1
6
  class SQS < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -18,7 +23,7 @@ class SQS < Mapper
18
23
  struct = OpenStruct.new(@client.get_queue_attributes({ queue_url: queue, attribute_names: ['All'] }).attributes.to_h)
19
24
  struct.type = 'queue'
20
25
  struct.arn = struct.QueueArn
21
- struct.policy = struct.delete_field('Policy').parse_policy
26
+ struct.policy = struct.Policy ? struct.delete_field('Policy').parse_policy : nil
22
27
 
23
28
  resources.push(struct.to_h)
24
29
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect SSM resources
5
+ #
1
6
  class SSM < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Support resources
5
+ #
1
6
  class Support < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -28,9 +33,7 @@ class Support < Mapper
28
33
  rescue Aws::Support::Errors::ServiceError => e
29
34
  log_error(e.code)
30
35
 
31
- unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
32
- raise e
33
- end
36
+ raise e unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
34
37
 
35
38
  [] # no Support subscription
36
39
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect Transfer resources
5
+ #
1
6
  class Transfer < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect WAFv2 resources
5
+ #
1
6
  class WAFV2 < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect WorkSpaces resources
5
+ #
1
6
  class WorkSpaces < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Collect XRay resources
5
+ #
1
6
  class XRay < Mapper
2
7
  #
3
8
  # Returns an array of resources.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Generic wrapper for service clients.
3
5
  #
@@ -64,14 +66,14 @@ class Mapper
64
66
  end
65
67
 
66
68
  def log(*msg)
67
- if @options.verbose
68
- puts _msg(msg).map { |x| "\x1b[32m#{x}\x1b[0m" }.join("\x1b[35m.\x1b[0m")
69
- end
69
+ return unless @options.verbose
70
+
71
+ puts _msg(msg).map { |x| "\x1b[32m#{x}\x1b[0m" }.join("\x1b[35m.\x1b[0m")
70
72
  end
71
73
 
72
74
  def log_error(*msg)
73
- if @options.verbose
74
- puts _msg(msg).map { |x| "\x1b[35m#{x}\x1b[0m" }.join("\x1b[32m.\x1b[0m")
75
- end
75
+ return unless @options.verbose
76
+
77
+ puts _msg(msg).map { |x| "\x1b[35m#{x}\x1b[0m" }.join("\x1b[32m.\x1b[0m")
76
78
  end
77
79
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Parse and unescape AWS policy document string
3
5
  #
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Command line options parser
5
+ #
3
6
  class Parser
4
7
  DEFAULT_CONFIG_FILE = nil
5
8
  DEFAULT_OUTPUT_FILE = File.expand_path(File.join(Dir.pwd, 'output.json')).freeze
@@ -97,16 +100,12 @@ class Parser
97
100
 
98
101
  # output format
99
102
  opts.on('-f', '--format [FORMAT]', 'Specify output format (default: aws)') do |file|
100
- if %w[aws custom].include?(file.downcase)
101
- args.output_format = file.downcase
102
- end
103
+ args.output_format = file.downcase if %w[aws custom].include?(file.downcase)
103
104
  end
104
105
 
105
106
  # threads
106
107
  opts.on('-t', '--threads [THREADS]', "Specify max threads (default: #{Parser::DEFAULT_THREADS}, max: 128)") do |threads|
107
- if (0..Parser::MAX_THREADS).include?(threads.to_i)
108
- args.threads = threads.to_i
109
- end
108
+ args.threads = threads.to_i if (0..Parser::MAX_THREADS).include?(threads.to_i)
110
109
  end
111
110
 
112
111
  # collect EC2 instance user data
@@ -1,3 +1,3 @@
1
1
  module AwsRecon
2
- VERSION = "0.2.25"
2
+ VERSION = "0.2.30"
3
3
  end
data/readme.md CHANGED
@@ -7,9 +7,9 @@ A multi-threaded AWS inventory collection tool.
7
7
 
8
8
  The [creators](https://darkbit.io) of this tool have a recurring need to be able to efficiently collect a large amount of AWS resource attributes and metadata to help clients understand their cloud security posture.
9
9
 
10
- There are a handful of tools (e.g. [AWS Config](https://aws.amazon.com/config), [CloudMapper](https://github.com/duo-labs/cloudmapper), [CloudSploit](https://github.com/cloudsploit/scans), [Prowler](https://github.com/toniblyx/prowler)) that do some form of resource collection to support other functions. But we found we needed broader coverage and more details at a per-service level. We also needed a consistent and structured format that allowed for integration with our other systems and tooling.
10
+ Existing tools (e.g. [AWS Config](https://aws.amazon.com/config)) that do some form of resource collection lack the coverage and specificity we needed. We also needed a tool that produced consistent output that was easily consumed by other tools/systems.
11
11
 
12
- Enter AWS Recon, multi-threaded AWS inventory collection tool written in plain Ruby. Though most AWS tooling tends to be dominated by Python, the [Ruby SDK](https://aws.amazon.com/sdk-for-ruby/) is quite mature and capable. The maintainers of the Ruby SDK have done a fantastic job making it easy to handle automatic retries, paging of large responses, and threading huge numbers of requests.
12
+ Enter AWS Recon, multi-threaded AWS inventory collection tool written in plain Ruby. Though Python tends to dominate the AWS tooling landscape, the [Ruby SDK](https://aws.amazon.com/sdk-for-ruby/) has a few convenient advantages over the [other](https://aws.amazon.com/sdk-for-node-js/) [AWS](https://aws.amazon.com/sdk-for-python/) [SDKs](https://aws.amazon.com/sdk-for-go/) we tested. Specifically, easy handling of automatic retries, paging of large responses, and - with some help - threading huge numbers of requests.
13
13
 
14
14
  ## Project Goals
15
15
 
@@ -23,24 +23,44 @@ Enter AWS Recon, multi-threaded AWS inventory collection tool written in plain R
23
23
 
24
24
  ### Requirements
25
25
 
26
- Ruby 2.5.x or 2.6.x (developed and tested with 2.6.5)
26
+ AWS Recon needs an AWS account role or credentials with `ReadOnlyAccess`. Full `AdministratorAccess` is over-privileged, but will work as well. The `SecurityAudit` policy is **not** sufficient as it omits access to many services.
27
+
28
+ #### Running via Docker
29
+
30
+ Use Docker version 19.x or above to run the pre-built image without having to install anything.
31
+
32
+ #### Running locally via Ruby
33
+
34
+ If you already have Ruby installed (2.5.x or 2.6.x), you may want to install the Ruby gem.
27
35
 
28
36
  ### Installation
29
37
 
30
- AWS Recon can be run locally by installing the Ruby gem, or via a Docker container.
38
+ AWS Recon can be run locally via a Docker container or by installing the Ruby gem.
39
+
40
+ To run via a Docker a container, pass the necessary AWS credentials into the Docker `run` command. For example:
41
+
42
+ ```
43
+ $ docker run -t --rm \
44
+ -e AWS_REGION \
45
+ -e AWS_ACCESS_KEY_ID \
46
+ -e AWS_SECRET_ACCESS_KEY \
47
+ -e AWS_SESSION_TOKEN \
48
+ -v $(pwd)/output.json:/recon/output.json \
49
+ darkbitio/aws_recon:latest \
50
+ aws_recon -v -s EC2 -r global,us-east-1,us-east-2
51
+ ```
31
52
 
32
53
  To run locally, first install the gem:
33
54
 
34
55
  ```
35
56
  $ gem install aws_recon
36
- Fetching aws_recon-0.2.8.gem
37
- Fetching aws-sdk-resources-3.76.0.gem
57
+ Fetching aws_recon-0.2.28.gem
38
58
  Fetching aws-sdk-3.0.1.gem
39
- Fetching parallel-1.19.2.gem
59
+ Fetching parallel-1.20.1.gem
40
60
  ...
41
61
  Successfully installed aws-sdk-3.0.1
42
- Successfully installed parallel-1.19.2
43
- Successfully installed aws_recon-0.2.8
62
+ Successfully installed parallel-1.20.1
63
+ Successfully installed aws_recon-0.2.28
44
64
  ```
45
65
 
46
66
  Or add it to your Gemfile using `bundle`:
@@ -51,27 +71,13 @@ Fetching gem metadata from https://rubygems.org/
51
71
  Resolving dependencies...
52
72
  ...
53
73
  Using aws-sdk 3.0.1
54
- Using parallel 1.19.2
55
- Using aws_recon 0.2.8
74
+ Using parallel-1.20.1
75
+ Using aws_recon 0.2.28
56
76
  ```
57
77
 
58
- To run via a Docker a container, pass the necessary AWS credentials into the Docker `run` command. For example:
59
-
60
- ```
61
- $ docker run -t --rm \
62
- -e AWS_REGION \
63
- -e AWS_ACCESS_KEY_ID \
64
- -e AWS_SECRET_ACCESS_KEY \
65
- -e AWS_SESSION_TOKEN \
66
- -v $(pwd)/output.json:/recon/output.json \
67
- darkbitio/aws_recon:latest \
68
- aws_recon -v -s EC2 -r global,us-east-1,us-east-2
69
- ```
70
-
71
-
72
78
  ## Usage
73
79
 
74
- AWS Recon will leverage any AWS credentials currently available to the environment it runs in. If you are collecting from multiple accounts, you may want to leverage something like [aws-vault](https://github.com/99designs/aws-vault) to manage different credentials.
80
+ AWS Recon will leverage any AWS credentials (see [requirements](#requirements)) currently available to the environment it runs in. If you are collecting from multiple accounts, you may want to leverage something like [aws-vault](https://github.com/99designs/aws-vault) to manage different credentials.
75
81
 
76
82
  ```
77
83
  $ aws-vault exec profile -- aws_recon
@@ -95,7 +101,7 @@ $ aws-vault exec <vault_profile> -- docker run -t --rm \
95
101
  aws_recon -j -s EC2 -r global,us-east-1,us-east-2
96
102
  ```
97
103
 
98
- To run from a Docker container using `aws-vault` managed credentials and output to a file, you will need to satisfy a couple of requirements. First, Docker needs access to bind mount the path you specify (or a parent path above). Second, you need to create an empty file to save the output into (e.g. `output.json`). This is because we are only mounting that one file into the Docker container at run time. For example:
104
+ To run from a Docker container using `aws-vault` managed credentials and output to a file, you will need to satisfy a couple of requirements. First, Docker needs access to bind mount the path you specify (or a parent path above). Second, you need to create an empty file to save the output into (e.g. `output.json`). This is because only that one file is mounted into the Docker container at run time. For example:
99
105
 
100
106
  Create an empty file.
101
107
 
@@ -159,22 +165,44 @@ $ AWS_PROFILE=<profile> aws_recon -s S3,EC2 -r global,us-east-1,us-east-2
159
165
  $ AWS_PROFILE=<profile> aws_recon --services S3,EC2 --regions global,us-east-1,us-east-2
160
166
  ```
161
167
 
162
- Example [OpenCSPM](https://github.com/OpenCSPM/opencspm) formatted output.
168
+ Example [OpenCSPM](https://github.com/OpenCSPM/opencspm) formatted (NDJSON) output.
163
169
 
164
170
  ```
165
- $ AWS_PROFILE=<profile> aws_recon -s S3,EC2 -r global,us-east-1,us-east-2 -f custom > output.json
171
+ $ AWS_PROFILE=<profile> aws_recon -j \
172
+ -s S3,EC2 \
173
+ -r global,us-east-1,us-east-2 \
174
+ -f custom > output.json
166
175
  ```
167
176
 
168
177
  #### Errors
169
178
 
170
- An exception will be raised on `AccessDeniedException` errors. This typically means your user/role doesn't have the necessary permissions to get/list/describe for that service. These exceptions are raised so troubleshooting access issues is easier.
179
+ API exceptions related to permissions are silently ignored in most cases. These errors are usually due to one of these cases:
180
+
181
+ - using a role without sufficient permissions
182
+ - querying an account with SCPs in place that prevent usage of certain services
183
+ - trying to query a service that isn't enabled/available in your region/account
184
+
185
+ In `verbose` mode, you will see exception logs in the output:
186
+
187
+ ```
188
+ t2.us-east-1.EC2.describe_subnets.0
189
+ t4.us-east-1.SSM.describe_instance_information.0
190
+ t6.us-east-1.SecurityHub.InvalidAccessException <-----
191
+ t2.us-east-1.EC2.describe_addresses.0
192
+ t4.us-east-1.SSM.describe_parameters.0
193
+ t1.us-east-1.GuardDuty.list_detectors.0
194
+ ```
195
+
196
+ Use the `-q` command line option to re-raise these exceptions so troubleshooting access issues is easier.
171
197
 
172
198
  ```
173
199
  Traceback (most recent call last):
174
- arn:aws:sts::1234567890:assumed-role/role/9876543210 is not authorized to perform: codepipeline:GetPipeline on resource: arn:aws:codepipeline:us-west-2:876543210123:pipeline (Aws::CodePipeline::Errors::AccessDeniedException)
200
+ arn:aws:sts::1234567890:assumed-role/role/my-audit-role is not authorized to perform:
201
+ codepipeline:GetPipeline on resource: arn:aws:codepipeline:us-west-2:1234567890:pipeline
202
+ (Aws::CodePipeline::Errors::AccessDeniedException)
175
203
  ```
176
204
 
177
- The exact API operation that triggered the exception is indicated on the last line of the stack trace. If you can't resolve the necessary access, you should exclude those services with `-x` or `--not-services` so the collection can continue.
205
+ The exact API operation that triggered the exception is indicated on the last line of the stack trace. If you can't resolve the necessary access, you should exclude those services with `-x` or `--not-services`, or leave off the `-q` option so the collection can continue.
178
206
 
179
207
  ### Threads
180
208
 
@@ -184,14 +212,20 @@ For global services like IAM, Shield, and Support, requests are not multi-thread
184
212
 
185
213
  For regional services, a thread (up to the thread limit) is spawned for each service in a region. By default, up to 8 threads will be used. If your account has resources spread across many regions, you may see a speed improvement by increasing threads with `-t X`, where `X` is the number of threads.
186
214
 
215
+ ### Performance
216
+
217
+ AWS Recon will make a minimum of ~2,000 API calls in a new/empty account, just to query the supported services in all 20 standard (non-GovCloud, non-China) regions. It is very likely to encounter API rate-limiting (throttling) on large accounts if you enable more threads than the default (8).
218
+
219
+ Recon will automatically backoff and respect the retry limits in the API response. If you observe long pauses during collection, this is likely what is happening. Retry collection with the `-d` or `--debug` option to observe the wire trace and see if you're being throttled. Consider using fewer threads or requesting higher rate limits from AWS if you are regularly getting rate-limited.
220
+
187
221
  ### Options
188
222
 
189
- Most users will want to limit collection to relevant services and regions. Running without any options will attempt to collect all resources from all 16 regular regions.
223
+ Most users will want to limit collection to relevant services and regions. Running without any exclusions will attempt to collect all resources from all regions enabled for the account.
190
224
 
191
225
  ```
192
226
  $ aws_recon -h
193
227
 
194
- AWS Recon - AWS Inventory Collector (0.2.8)
228
+ AWS Recon - AWS Inventory Collector (0.2.28)
195
229
 
196
230
  Usage: aws_recon [options]
197
231
  -r, --regions [REGIONS] Regions to scan, separated by comma (default: all)
@@ -204,8 +238,10 @@ Usage: aws_recon [options]
204
238
  -t, --threads [THREADS] Specify max threads (default: 8, max: 128)
205
239
  -u, --user-data Collect EC2 instance user data (default: false)
206
240
  -z, --skip-slow Skip slow operations (default: false)
241
+ -g, --skip-credential-report Skip generating IAM credential report (default: false)
207
242
  -j, --stream-output Stream JSON lines to stdout (default: false)
208
243
  -v, --verbose Output client progress and current operation
244
+ -q, --quit-on-exception Stop collection if an API error is encountered (default: false)
209
245
  -d, --debug Output debug with wire trace info
210
246
  -h, --help Print this help information
211
247
 
@@ -217,7 +253,7 @@ Output is always some form of JSON - either JSON lines or plain JSON. The output
217
253
 
218
254
  ## Support for Manually Enabled Regions
219
255
 
220
- If you have enabled manually enabled regions:
256
+ If you have enabled **manually enabled regions**:
221
257
 
222
258
  - me-south-1 - Middle East (Bahrain)
223
259
  - af-south-1 - Africa (Cape Town)
@@ -228,7 +264,7 @@ and you are using STS to assume a role into an account, you will need to [enable
228
264
 
229
265
  > Version 1 tokens are valid only in AWS Regions that are available by default. These tokens do not work in manually enabled Regions, such as Asia Pacific (Hong Kong). Version 2 tokens are valid in all Regions. However, version 2 tokens are longer and might affect systems where you temporarily store tokens.
230
266
 
231
- If you are using a static access key/secret, you can collect from these regions with either `v1` or `v2` STS tokens.
267
+ If you are using a static access key/secret, you can collect from these regions regardless of STS token version.
232
268
 
233
269
  ## Supported Services & Resources
234
270
 
@@ -324,11 +360,8 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
324
360
 
325
361
  ### TODO
326
362
 
327
- - [ ] Optionally suppress AWS API errors instead of re-raising them
328
- - [x] Package as a gem
329
363
  - [ ] Test coverage with AWS SDK stubbed resources
330
364
 
331
-
332
365
  ## Kudos
333
366
 
334
367
  AWS Recon was inspired by the excellent work of the people and teams behind these tools:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws_recon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.25
4
+ version: 0.2.30
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Larsen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-12-06 00:00:00.000000000 Z
12
+ date: 2021-01-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -197,7 +197,7 @@ files:
197
197
  - lib/aws_recon/collectors/codepipeline.rb
198
198
  - lib/aws_recon/collectors/configservice.rb
199
199
  - lib/aws_recon/collectors/directconnect.rb
200
- - lib/aws_recon/collectors/directyservice.rb
200
+ - lib/aws_recon/collectors/directoryservice.rb
201
201
  - lib/aws_recon/collectors/dms.rb
202
202
  - lib/aws_recon/collectors/dynamodb.rb
203
203
  - lib/aws_recon/collectors/ec2.rb