Class: InsightVMApi

Inherits:
Object
  • Object
show all
Defined in:
lib/domain/tag/api.rb,
lib/domain/site/api.rb,
lib/domain/asset/api.rb,
lib/domain/report/api.rb,
lib/domain/country/api.rb,
lib/domain/software/api.rb,
lib/service/api/insightvm.rb,
lib/domain/asset_group/api.rb,
lib/domain/scan_engine/api.rb,
lib/domain/site_target/api.rb,
lib/domain/scan_schedule/api.rb,
lib/domain/vulnerability/api.rb,
lib/domain/operating_system/api.rb,
lib/domain/scan_engine_pool/api.rb,
lib/domain/shared_credential/api.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_url, username, password) ⇒ InsightVMApi

Returns a new instance of InsightVMApi.



61
62
63
64
65
66
67
# File 'lib/service/api/insightvm.rb', line 61

def initialize(base_url, username, password)
  @base_url = URI(base_url)
  @http = Net::HTTP.new(@base_url.host, @base_url.port)
  @http.use_ssl = true
  @http.verify_mode = OpenSSL::SSL::VERIFY_NONE # Reminder: Adjust for production use
  @base_auth = ['Basic', Base64.strict_encode64("#{username}:#{password}")].join(' ')
end

Instance Attribute Details

#base_authObject (readonly)

Returns the value of attribute base_auth.



12
13
14
# File 'lib/service/api/insightvm.rb', line 12

def base_auth
  @base_auth
end

#base_urlObject (readonly)

Returns the value of attribute base_url.



12
13
14
# File 'lib/service/api/insightvm.rb', line 12

def base_url
  @base_url
end

#httpObject (readonly)

Returns the value of attribute http.



12
13
14
# File 'lib/service/api/insightvm.rb', line 12

def http
  @http
end

Instance Method Details

#_fetch_all_scan_engines_poolsObject



17
18
19
20
21
22
23
# File 'lib/domain/scan_engine_pool/api.rb', line 17

def _fetch_all_scan_engines_pools
  @scan_engine_pools = []
  fetch_scan_engine_pools do |scan_engine_pool|
    @scan_engine_pools << scan_engine_pool
  end
  @scan_engine_pools
end

#add_shared_credentials(site) ⇒ Object



260
261
262
263
264
265
266
267
# File 'lib/domain/site/api.rb', line 260

def add_shared_credentials(site)
  site_ids = [site.id]
  shared_credentials = fetch_site_cyberark_credentials(site)
  shared_credentials.each do |credential|
    puts "#{site.name} Shared Credential #{credential.name}"
    update_shared_credential_sites(credential:, site_ids:)
  end
end

#add_site_scan_engine_pool(site_id:, pool_id:) ⇒ Object



114
115
116
117
118
119
120
121
122
# File 'lib/domain/scan_engine/api.rb', line 114

def add_site_scan_engine_pool(site_id:, pool_id:)
  # retrieve the credential_id
  pool = fetch_scan_engine_pool(pool_id)
  return if pool.sites.include?(site_id)

  pool.sites += [site_id]
  endpoint = "/scan_engine_pool/#{pool_id}"
  put(endpoint, pool)
end

#add_utr_tags(vulnerability_id:, tag_ids:) ⇒ Object



269
270
271
272
273
# File 'lib/domain/site/api.rb', line 269

def add_utr_tags(site_id:, tag_ids:)
  tag_ids.each do |tag_id|
    put("/sites/#{site_id}/tags/#{tag_id}", nil)
  end
end

#all_asset_groups(opts) ⇒ Object



24
25
26
27
28
29
30
# File 'lib/domain/asset_group/api.rb', line 24

def all_asset_groups(opts)
  asset_groups = []
  fetch_asset_groups(opts) do |e|
    asset_groups << e
  end
  asset_groups
end

#all_scan_enginesObject



19
20
21
22
23
24
25
# File 'lib/domain/scan_engine/api.rb', line 19

def all_scan_engines
  engines = []
  fetch_scan_engines do |e|
    engines << e
  end
  engines
end

#all_shared_credentialsObject



10
11
12
# File 'lib/domain/shared_credential/api.rb', line 10

def all_shared_credentials
  @all_shared_credentials ||= fetch_all_shared_credentials
end

#all_site_scan_schedules(site_id:) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/domain/scan_schedule/api.rb', line 13

def all_site_scan_schedules(site_id:)
  schedules = []
  fetch_scan_schedules(site_id:) do |e|
    schedules << e
  end
  schedules
end

#all_tagsObject



12
13
14
15
16
17
18
# File 'lib/domain/tag/api.rb', line 12

def all_tags
  tags = []
  fetch_tags do |e|
    tags << e
  end
  tags
end

#append_new_status(engines:, csv_file:, previous_status:) ⇒ Object

append the new engine status in the csv_file only if it is different from the previous status



54
55
56
57
58
59
60
61
62
# File 'lib/domain/scan_engine/api.rb', line 54

def append_new_status(engines:, csv_file:, previous_status:)
  CSV.open(csv_file, 'a') do |csv|
    engines.each do |engine|
      next if previous_status[engine.id] == engine.up?

      csv << [Time.now.strftime('%Y-%m-%dT%H:%M'), engine.id, engine.up?.to_s]
    end
  end
end

#create_asset_group(name:, description: nil, type: 'dynamic', search_criteria: { match: 'all', filters: [] }) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/domain/asset_group/api.rb', line 80

def create_asset_group(
  name:,
  description: nil,
  type: 'dynamic',
  search_criteria: { match: 'all', filters: [] }
)
  params = {
    description:,
    name:,
    searchCriteria: search_criteria,
    type:
  }
  result = post('/asset_groups', params)
  result&.dig('id')
end

#create_asset_group_for(site_id:, site_name:) ⇒ Object



69
70
71
72
73
74
75
76
77
78
# File 'lib/domain/asset_group/api.rb', line 69

def create_asset_group_for(site_id:, site_name:)
  filter = SearchCriteria::Filter.from_site_id(site_id)
  search_criteria = { match: 'all', filters: [filter.to_json] }
  create_asset_group(
    name: site_name,
    description: "Access group for site #{site_id}",
    type: 'dynamic',
    search_criteria:
  )
end

#create_report(name:, description:, engine_id:, scan_template_id:, importance: 'normal', included_targets: [], excluded_targets: [], included_asset_group_ids: [], excluded_asset_group_ids: []) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/domain/report/api.rb', line 73

def create_report(
  name:,
  description:,
  engine_id:,
  scan_template_id:,
  importance: 'normal',
  included_targets: [],
  excluded_targets: [],
  included_asset_group_ids: [],
  excluded_asset_group_ids: []
)
  # Construct the request body
  params = {
    name:,
    description:,
    importance:,
    engineId: engine_id,
    scanTemplateId: scan_template_id,
    scan: {
      assets: {
        includedTargets: {
          addresses: included_targets
        },
        excludedTargets: {
          addresses: excluded_targets
        },
        includedAssetGroups: {
          assetGroupIDs: included_asset_group_ids
        },
        excludedAssetGroups: {
          assetGroupIDs: excluded_asset_group_ids
        }
      }
    }
  }
  result = post('/reports', params)
  result&.dig('id')
end

#create_scan_schedule(site_id:, scan_engine_id:, scan_template_id:, duration:, repeat:, start:, on_scan_repeat: ScanSchedule::OnRepeat::RESTART, scan_name: nil, excluded_asset_group_ids: [], included_asset_group_ids: [], excluded_targets: [], included_targets: [], enabled: false) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/domain/scan_schedule/api.rb', line 66

def create_scan_schedule(
  site_id:,
  scan_engine_id:,
  scan_template_id:,
  duration:,
  repeat:,
  start:,
  on_scan_repeat: ScanSchedule::OnRepeat::RESTART,
  scan_name: nil, # required ISO8601 string
  excluded_asset_group_ids: [],
  included_asset_group_ids: [],
  excluded_targets: [],
  included_targets: [],
  enabled: false
)
  params = {
    assets: {
      excludedAssetGroups: {
        assetGroupIDs: excluded_asset_group_ids
      },
      excludedTargets: {
        addresses: excluded_targets
      },
      includedAssetGroups: {
        assetGroupIDs: included_asset_group_ids
      },
      includedTargets: {
        addresses: included_targets
      }
    },
    duration:,
    enabled:,
    start:,
    repeat: JSON.parse(repeat.to_json),
    scanEngineId: scan_engine_id,
    scanTemplateId: scan_template_id,
    onScanRepeat: on_scan_repeat,
    scanName: scan_name

  }
  result = post("/sites/#{site_id}/scan_schedules", params)
  result&.dig('id')
end

#create_site(name:, description:, engine_id:, scan_template_id:, importance: 'normal', included_targets: [], excluded_targets: [], included_asset_group_ids: [], excluded_asset_group_ids: []) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/domain/site/api.rb', line 60

def create_site(
  name:,
  description:,
  engine_id:,
  scan_template_id:,
  importance: 'normal',
  included_targets: [],
  excluded_targets: [],
  included_asset_group_ids: [],
  excluded_asset_group_ids: []
)
  # Construct the request body
  params = {
    name:,
    description:,
    importance:,
    engineId: engine_id,
    scanTemplateId: scan_template_id,
    scan: {
      assets: {
        includedTargets: {
          addresses: included_targets
        },
        excludedTargets: {
          addresses: excluded_targets
        },
        includedAssetGroups: {
          assetGroupIDs: included_asset_group_ids
        },
        excludedAssetGroups: {
          assetGroupIDs: excluded_asset_group_ids
        }
      }
    }
  }
  result = post('/sites', params)
  result&.dig('id')
end

#create_tag(name:, color: 'default', risk_modifier: 1.0, type: 'custom', source: 'custom') ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/domain/tag/api.rb', line 57

def create_tag(
  name:,
  color: 'default',
  risk_modifier: 1.0,
  type: 'custom',
  source: 'custom'
)
  params = {
    color:,
    name:,
    riskModifier: risk_modifier,
    type:,
    source:
  }
  result = post('/tags', params)
  result&.dig('id')
end

#create_utr_report(name:, description:, targets:, engine_id:, scan_template_id:) ⇒ Object



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/domain/report/api.rb', line 274

def create_utr_report(
  name:,
  description:,
  targets:,
  engine_id:,
  scan_template_id:
)
  create_report(
    name:,
    description:,
    importance: 'high',
    engine_id:,
    scan_template_id:,
    included_targets: targets
  )
end

#create_utr_report_from(report_name:, cmdb_assets:, cached_tags: {}) ⇒ Object

return report.id if success only return the onboard assets



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/domain/report/api.rb', line 200

def create_utr_report_from(
  report_name:, cmdb_assets:, cached_tags: {}
)
  assets = cmdb_assets.select { |asset| asset.report_name == report_name }
                      .select(&:onboard?)

  return if assets.empty?

  country = assets.first.country
  targets = assets.map(&:fqdn)
  scan_engine_pool = fetch_country_scan_engine_pools(country)
  # puts "Scan engine pool #{scan_engine_pool}"
  engine_id = scan_engine_pool[:id]
  scan_template_id = fetch_discovery_scan_template_id(country)
  puts
  puts '-' * 40
  puts "Report #{report_name}\nTargets: #{targets.length} #{targets.join(' ')}"
  puts '-' * 40

  # TODO: check if the report already exists
  report_id = create_utr_report(
    name: report_name,
    description: report_name,
    targets:,
    engine_id:,
    scan_template_id:
  )
  if report_id.nil?
    puts "Report #{report_name} already exists!"
    return
  end

  # add report to shared credential
  report = fetch_report(report_id)
  add_shared_credentials(report)

  # tag assets with business unit code, sub_area, app + utr,
  tag_names = assets.first.utr_tag_names
  tags = tag_names.map do |tag_name|
    puts "\tAdd tag: #{tag_name}"
    get_or_create_tag(name: tag_name, cached_tags:)
  end
  tag_ids = tags.map(&:id)

  add_utr_tags(report_id:, tag_ids:)
  report_id
end

#create_utr_report_schedule(report_id:) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/domain/report/api.rb', line 176

def create_utr_report_schedule(report_id:)
  report = fetch_report(report_id)
  raise 'The report is not a valid UTR report' unless report.utr?

  scan_name = report.name
  report.utr_digits
  slot = scan_slot(report.utr_digits)
  country_code = report.country_code
  scan_template_id = report.scan_template_id
  duration_in_hours = 2

  create_weekly_scan(
    country_code:,
    day_of_week: slot[:day_of_week],
    start_hour: slot[:start_time],
    duration_in_hours:,
    report_id:,
    scan_name:,
    scan_template_id:
  )
end

#create_utr_reports_for(business_unit:, cmdb_assets:, cached_tags: {}, starts_discovery: true) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/domain/report/api.rb', line 112

def create_utr_reports_for(
  business_unit:,
  cmdb_assets:,
  cached_tags: {},
  starts_discovery: true
)
  assets = cmdb_assets.select { |asset| asset.business_unit == business_unit }
  report_names = assets.map(&:report_name).uniq
  report_names.each do |report_name|
    report_id = create_utr_report_from(
      report_name:,
      cmdb_assets: assets,
      cached_tags:
    )
    if report_id.nil?
      puts "Cannot create #{report_name} report"
      next
    end

    puts "\tCreate asset group: #{report_name}"
    create_asset_group_for(report_id:, report_name:)

    puts "\tSchedule the scan"
    # TODO

    next unless starts_discovery

    puts "\tStart discovery scan"
    starts_discovery_scan(report_id:)
  end
end

#create_utr_site(name:, description:, targets:, engine_id:, scan_template_id:) ⇒ Object



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/domain/site/api.rb', line 309

def create_utr_site(
  name:,
  description:,
  targets:,
  engine_id:,
  scan_template_id:
)
  create_site(
    name:,
    description:,
    importance: 'high',
    engine_id:,
    scan_template_id:,
    included_targets: targets
  )
end

#create_utr_site_from(site_name:, cmdb_assets:, cached_tags: {}) ⇒ Object

return site.id if success only return the onboard assets



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/domain/site/api.rb', line 204

def create_utr_site_from(
  site_name:, cmdb_assets:, cached_tags: {}
)
  assets = cmdb_assets.select { |asset| asset.site_name == site_name }
                      .select(&:onboard?)

  return if assets.empty?

  country = assets.first.country
  targets = assets.map(&:fqdn)
  scan_engine_pool = fetch_country_scan_engine_pools(country)
  # puts "Scan engine pool #{scan_engine_pool}"
  engine_id = scan_engine_pool[:id]
  scan_template_id = fetch_discovery_scan_template_id(country)
  puts
  puts '-' * 40
  puts "Site #{site_name}\nTargets: #{targets.length} #{targets.join(' ')}"
  puts '-' * 40

  # TODO: check if the site already exists
  site_id = create_utr_site(
    name: site_name,
    description: site_name,
    targets:,
    engine_id:,
    scan_template_id:
  )
  if site_id.nil?
    puts "Site #{site_name} already exists!"
    return
  end

  # add site to shared credential
  site = fetch_site(site_id)
  add_shared_credentials(site)

  # tag assets with business unit code, sub_area, app + utr,
  tag_names = assets.first.utr_tag_names
  tags = tag_names.map do |tag_name|
    puts "\tAdd tag: #{tag_name}"
    get_or_create_tag(name: tag_name, cached_tags:)
  end
  tag_ids = tags.map(&:id)

  add_utr_tags(site_id:, tag_ids:)
  site_id
end

#create_utr_site_schedule(site_id:) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/domain/site/api.rb', line 173

def create_utr_site_schedule(site_id:)
  site = fetch_site(site_id)
  raise 'The site is not a valid UTR site' unless site.utr?

  scan_name = site.name
  slot = scan_slot(site.utr_digits)
  country_code = site.country_code
  scan_template_id = site.scan_template_id
  duration_in_hours = 2

  create_weekly_scan(
    country_code:,
    day_of_week: slot[:day_of_week],
    start_hour: slot[:start_time],
    duration_in_hours:,
    site_id:,
    scan_name:,
    scan_template_id:
  )
end

#create_utr_sites_for(business_unit:, cmdb_assets:, cached_tags: {}, starts_discovery: true) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/domain/site/api.rb', line 99

def create_utr_sites_for(
  business_unit:,
  cmdb_assets:,
  cached_tags: {},
  starts_discovery: true
)
  assets = cmdb_assets.select { |asset| asset.business_unit == business_unit }
  site_names = assets.map(&:site_name).uniq
  site_names.each do |site_name|
    site_id = create_utr_site_from(
      site_name:,
      cmdb_assets: assets,
      cached_tags:
    )
    if site_id.nil?
      puts "Cannot create #{site_name} site"
      next
    end

    puts "\tCreate asset group: #{site_name}"
    create_asset_group_for(site_id:, site_name:)

    puts "\tSchedule the scan"
    # TODO

    next unless starts_discovery

    puts "\tStart discovery scan"
    starts_discovery_scan(site_id:)
  end
end

#create_utr_vulnerabilities_for(business_unit:, cmdb_assets:, cached_tags: {}, starts_discovery: true) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/domain/vulnerability/api.rb', line 92

def create_utr_vulnerabilities_for(
  business_unit:,
  cmdb_assets:,
  cached_tags: {},
  starts_discovery: true
)
  assets = cmdb_assets.select { |asset| asset.business_unit == business_unit }
  vulnerability_names = assets.map(&:vulnerability_name).uniq
  vulnerability_names.each do |vulnerability_name|
    vulnerability_id = create_utr_vulnerability_from(
      vulnerability_name:,
      cmdb_assets: assets,
      cached_tags:
    )
    if vulnerability_id.nil?
      puts "Cannot create #{vulnerability_name} vulnerability"
      next
    end

    puts "\tCreate asset group: #{vulnerability_name}"
    create_asset_group_for(vulnerability_id:, vulnerability_name:)

    puts "\tSchedule the scan"
    # TODO

    next unless starts_discovery

    puts "\tStart discovery scan"
    starts_discovery_scan(vulnerability_id:)
  end
end

#create_utr_vulnerability(name:, description:, targets:, engine_id:, scan_template_id:) ⇒ Object



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/domain/vulnerability/api.rb', line 261

def create_utr_vulnerability(
  name:,
  description:,
  targets:,
  engine_id:,
  scan_template_id:
)
  create_vulnerability(
    name:,
    description:,
    importance: 'high',
    engine_id:,
    scan_template_id:,
    included_targets: targets
  )
end

#create_utr_vulnerability_from(vulnerability_name:, cmdb_assets:, cached_tags: {}) ⇒ Object

return vulnerability.id if success only return the onboard assets



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/domain/vulnerability/api.rb', line 171

def create_utr_vulnerability_from(
  vulnerability_name:, cmdb_assets:, cached_tags: {}
)
  assets = cmdb_assets.select { |asset| asset.vulnerability_name == vulnerability_name }
                      .select(&:onboard?)

  return if assets.empty?

  country = assets.first.country
  targets = assets.map(&:fqdn)
  scan_engine_pool = fetch_country_scan_engine_pools(country)
  # puts "Scan engine pool #{scan_engine_pool}"
  engine_id = scan_engine_pool[:id]
  scan_template_id = fetch_discovery_scan_template_id(country)
  puts
  puts '-' * 40
  puts "Vulnerability #{vulnerability_name}\nTargets: #{targets.length} #{targets.join(' ')}"
  puts '-' * 40

  # TODO: check if the vulnerability already exists
  vulnerability_id = create_utr_vulnerability(
    name: vulnerability_name,
    description: vulnerability_name,
    targets:,
    engine_id:,
    scan_template_id:
  )
  if vulnerability_id.nil?
    puts "Vulnerability #{vulnerability_name} already exists!"
    return
  end

  # add vulnerability credential
  shared_credential = fetch_cyberark(country)
  puts "\tAdd credential: #{shared_credential.name}"
  credential_id = shared_credential.id
  add_vulnerability_shared_credentials(vulnerability_id:, credential_id:)

  # tag assets with business unit code, sub_area, app + utr,
  tag_names = assets.first.utr_tag_names
  tags = tag_names.map do |tag_name|
    puts "\tAdd tag: #{tag_name}"
    get_or_create_tag(name: tag_name, cached_tags:)
  end
  tag_ids = tags.map(&:id)

  add_utr_tags(vulnerability_id:, tag_ids:)
  vulnerability_id
end

#create_utr_vulnerability_schedule(vulnerability_id:) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/domain/vulnerability/api.rb', line 141

def create_utr_vulnerability_schedule(vulnerability_id:)
  vulnerability = fetch_vulnerability(vulnerability_id)
  raise 'The vulnerability is not a valid UTR vulnerability' unless vulnerability.utr?

  scan_name = vulnerability.name
  vulnerability.utr_digits
  slot = scan_slot(vulnerability.utr_digits)
  scan_template_id = vulnerability.scan_template_id
  duration_in_hours = 2

  create_weekly_scan(
    day_of_week: slot[:day_of_week],
    start_time: slot[:start_time],
    duration_in_hours:,
    vulnerability_id:,
    scan_name:,
    scan_template_id:
  )
end

#create_vulnerability(name:, description:, engine_id:, scan_template_id:, importance: 'normal', included_targets: [], excluded_targets: [], included_asset_group_ids: [], excluded_asset_group_ids: []) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/domain/vulnerability/api.rb', line 53

def create_vulnerability(
  name:,
  description:,
  engine_id:,
  scan_template_id:,
  importance: 'normal',
  included_targets: [],
  excluded_targets: [],
  included_asset_group_ids: [],
  excluded_asset_group_ids: []
)
  # Construct the request body
  params = {
    name:,
    description:,
    importance:,
    engineId: engine_id,
    scanTemplateId: scan_template_id,
    scan: {
      assets: {
        includedTargets: {
          addresses: included_targets
        },
        excludedTargets: {
          addresses: excluded_targets
        },
        includedAssetGroups: {
          assetGroupIDs: included_asset_group_ids
        },
        excludedAssetGroups: {
          assetGroupIDs: excluded_asset_group_ids
        }
      }
    }
  }
  result = post('/vulnerabilities', params)
  result&.dig('id')
end

#create_weekly_scan(day_of_week:, start_hour:, country_code:, duration_in_hours:, site_id:, start_minute: 0, scan_name: nil, scan_engine_id: nil, scan_template_id: nil) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/domain/scan_schedule/api.rb', line 21

def create_weekly_scan(
  day_of_week:,
  start_hour:,
  country_code:, duration_in_hours:, site_id:, start_minute: 0,
  scan_name: nil,
  scan_engine_id: nil,
  scan_template_id: nil
)
  now = DateTime.now
  year = now.year
  month = now.month
  day = now.day
  start_time = DateTime.new(year, month, day, start_hour, start_minute)
  local_time = Service::DateTime.closest_day_of_week(start_time, day_of_week)
  start = Service::TimeZone.iso8601(country_code:,
                                    local_time:)
  puts start
  puts scan_name
  puts country_code
  repeat = ScanSchedule::Repeat.new(
    every: 'week',
    day_of_week:,
    interval: 1
  )
  create_scan_schedule(
    site_id:,
    duration: "PT#{duration_in_hours}H",
    scan_engine_id:,
    scan_template_id:,
    repeat:,
    start:,
    scan_name:
  )
end

#delete_asset(id) ⇒ Object



8
9
10
11
12
# File 'lib/domain/asset/api.rb', line 8

def delete_asset(id)
  raise 'Cannot delete asset without ID' if id.nil?

  delete('/assets', id)
end

#delete_asset_group_by(id: nil, name: nil) ⇒ Object



104
105
106
107
108
109
110
111
112
113
# File 'lib/domain/asset_group/api.rb', line 104

def delete_asset_group_by(id: nil, name: nil)
  raise 'Specify either id or name' if id.nil? && name.nil?

  if id
    delete_asset_group(id)
  else
    asset_group = fetch_asset_group_by_name(name)
    delete_asset_group(asset_group.site_idte_id) if asset_group
  end
end

#delete_assets(ids = [], threads = nil) ⇒ Object

Use Typhoeus for concurrency



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/domain/asset/api.rb', line 15

def delete_assets(ids = [], threads = nil)
  return if ids.empty?

  threads ||= ids.size**0.5
  size = ids.size / threads
  batches = ids.each_slice(size)
  hydra = Typhoeus::Hydra.new(max_concurrency: threads)
  batches.each do |batch|
    batch.each do |id|
      request = create_delete_request(id)
      hydra.queue(request)
    end
    # Run the queued batch of requests
    hydra.run
  end

  # TODO: give the number of deleted assets
  puts 'All assets in the group have been successfully deleted.'
end

#delete_report(report_id) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/domain/report/api.rb', line 259

def delete_report(report_id)
  raise 'Cannot delete report without id' if report_id.nil?

  report = fetch_report(report_id)
  raise "Report #{report_id} does not exist." if report.nil?

  puts 'Delete asset group with the same name as the report'
  delete_asset_group_by(name: report.name)

  puts "Delete assets from report #{report_id}"
  delete("/reports/#{report_id}/assets", '')
  puts "Delete report #{report_id}"
  delete('/reports', report_id)
end

#delete_report_by(id:, name:) ⇒ Object



248
249
250
251
252
253
254
255
256
257
# File 'lib/domain/report/api.rb', line 248

def delete_report_by(id:, name:)
  raise 'Specify either id or name' if id.nil? && name.nil?

  if id
    delete_report(id)
  else
    report = fetch_report_by_name(name)
    delete_report(report.id)
  end
end

#delete_scan_schedule(site_id:, schedule_id:) ⇒ Object



56
57
58
# File 'lib/domain/scan_schedule/api.rb', line 56

def delete_scan_schedule(site_id:, schedule_id:)
  delete("/sites/#{site_id}/scan_schedules", schedule_id)
end

#delete_site(site_id) ⇒ Object



294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/domain/site/api.rb', line 294

def delete_site(site_id)
  raise 'Cannot delete site without id' if site_id.nil?

  site = fetch_site(site_id)
  raise "Site #{site_id} does not exist." if site.nil?

  puts 'Delete asset group with the same name as the site'
  delete_asset_group_by(name: site.name)

  puts "Delete assets from site #{site_id}"
  delete("/sites/#{site_id}/assets", '')
  puts "Delete site #{site_id}"
  delete('/sites', site_id)
end

#delete_site_by(id:, name:) ⇒ Object



283
284
285
286
287
288
289
290
291
292
# File 'lib/domain/site/api.rb', line 283

def delete_site_by(id:, name:)
  raise 'Specify either id or name' if id.nil? && name.nil?

  if id
    delete_site(id)
  else
    site = fetch_site_by_name(name)
    delete_site(site.id)
  end
end

#delete_utr_report_schedules(report_id:) ⇒ Object



161
162
163
164
165
# File 'lib/domain/report/api.rb', line 161

def delete_utr_report_schedules(report_id:)
  fetch_report_scan_schedules(report_id:) do |scan_schedule|
    delete_scan_schedule(report_id:, schedule_id: scan_schedule.id)
  end
end

#delete_utr_reportsObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/domain/report/api.rb', line 34

def delete_utr_reports
  puts 'Fetching UTR reports can take up to 5 minutes, patience ...'
  reports = fetch_utr_reports
  # TODO: add progress bar
  raise 'No UTR reports were found.' if reports.empty?

  # TODO: ask for confirmation
  puts "#{reports.count} reports will be deleted. Are you sure?"
  reports.each do |report|
    next unless report.utr? # double-check

    puts "Deleting report #{report.name}"

    delete_report(report.id)
  end
end

#delete_utr_site_schedules(site_id:) ⇒ Object



148
149
150
151
152
# File 'lib/domain/site/api.rb', line 148

def delete_utr_site_schedules(site_id:)
  fetch_site_scan_schedules(site_id:) do |scan_schedule|
    delete_scan_schedule(site_id:, schedule_id: scan_schedule.id)
  end
end

#delete_utr_sitesObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/domain/site/api.rb', line 43

def delete_utr_sites
  puts 'Fetching UTR sites can take up to 5 minutes, patience ...'
  sites = fetch_utr_sites
  # TODO: add progress bar
  raise 'No UTR sites were found.' if sites.empty?

  # TODO: ask for confirmation
  puts "#{sites.count} sites will be deleted. Are you sure?"
  sites.each do |site|
    next unless site.utr? # double-check

    puts "Deleting site #{site.name}"

    delete_site(site.id)
  end
end

#delete_utr_vulnerabilitiesObject



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/domain/vulnerability/api.rb', line 36

def delete_utr_vulnerabilities
  puts 'Fetching UTR vulnerabilities can take up to 5 minutes, patience ...'
  vulnerabilities = fetch_utr_vulnerabilities
  # TODO: add progress bar
  raise 'No UTR vulnerabilities were found.' if vulnerabilities.empty?

  # TODO: ask for confirmation
  puts "#{vulnerabilities.count} vulnerabilities will be deleted. Are you sure?"
  vulnerabilities.each do |vulnerability|
    next unless vulnerability.utr? # double-check

    puts "Deleting vulnerability #{vulnerability.name}"

    delete_vulnerability(vulnerability.id)
  end
end

#delete_vulnerability(site_id) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/domain/vulnerability/api.rb', line 246

def delete_vulnerability(site_id)
  raise 'Cannot delete vulnerability without id' if site_id.nil?

  vulnerability = fetch_vulnerability(site_id)
  raise "Vulnerability #{site_id} does not exist." if vulnerability.nil?

  puts 'Delete asset group with the same name as the vulnerability'
  delete_asset_group_by(name: vulnerability.name)

  puts "Delete assets from vulnerability #{site_id}"
  delete("/vulnerabilities/#{site_id}/assets", '')
  puts "Delete vulnerability #{site_id}"
  delete('/vulnerabilities', site_id)
end

#delete_vulnerability_by(site_idte_idte_idte_id:, name:) ⇒ Object



235
236
237
238
239
240
241
242
243
244
# File 'lib/domain/vulnerability/api.rb', line 235

def delete_vulnerability_by(site_idte_idte_idte_id:, name:)
  raise 'Specify either id or name' if site_idte_id.nil? && name.nil?

  if site_idte_id
    delete_vulnerability(site_idte_id)
  else
    vulnerability = fetch_vulnerability_by_name(name)
    delete_vulnerability(vulnerability.site_idte_id)
  end
end

#engine_last_status_from(csv_file) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/domain/scan_engine/api.rb', line 39

def engine_last_status_from(csv_file)
  result = {}
  csv_data = CSV.read(csv_file, headers: true)
  sorted_data = csv_data.sort_by { |row| row['timestamp'] }

  sorted_data.each do |row|
    engine_id = row['engine_id'].to_i
    status = row['up'] == 'true'
    result[engine_id] = status
  end
  result
end

#fetch_all(endpoint, opts = {}, &block) ⇒ Object

Fetchs all resources

Parameters:

  • (String)
  • optional (Hash)

    parameters: page, size, type, name, sort



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/service/api/insightvm.rb', line 18

def fetch_all(endpoint, opts = {}, &block)
  params = {
    page: 0,
    size: 100,
    read_timeout: 5
  }.merge opts
  loop do
    full_url = @base_url.dup
    full_url.path += endpoint
    full_url.query = URI.encode_www_form(
      params
    )
    request = Net::HTTP::Get.new(full_url)
    request['Authorization'] = @base_auth
    @http.read_timeout = params[:read_timeout]
    response = @http.request(request)
    unless response.is_a?(Net::HTTPSuccess)
      puts "Error with code #{response.code}"
      break
    end

    json_response = JSON.parse(response.body)
    resources = json_response['resources']
    break if resources.nil?

    resources.each(&block) # equivalent to resources.each {|resource| yield resource}

    # Check if this is the last page
    current_page = json_response['page']&.[]('number') || 0
    pages = json_response['page']&.[]('totalPages') || 0
    break if params[:page] >= pages
    break if current_page + 1 >= pages

    params[:page] += 1
  end
rescue Net::ReadTimeout
  opts[:read_timeout] = 2 * params[:read_timeout] || 30
  raise 'Fail after multiple attempts' if opts[:read_timeout] > 300

  puts "Increase the timeout to #{opts[:read_timeout]}"
  fetch_all(endpoint, opts, &block)
end

#fetch_all_targets(site_id) ⇒ Object

Returns included and excluded targets for site id



19
20
21
22
23
# File 'lib/domain/site_target/api.rb', line 19

def fetch_all_targets(site_id)
  [true, false].reduce([]) do |accu, included|
    accu + fetch_site_targets(site_id, included:)
  end
end

#fetch_asset_group(site_id) ⇒ Object



96
97
98
99
100
101
102
# File 'lib/domain/asset_group/api.rb', line 96

def fetch_asset_group(site_id)
  result = nil
  fetch("/asset_groups/#{site_id}") do |data|
    result = AssetGroup.from_json(data)
  end
  result
end

#fetch_asset_group_assets(id, opts = { read_timeout: 200 }) ⇒ Object



15
16
17
18
19
20
21
22
# File 'lib/domain/asset_group/api.rb', line 15

def fetch_asset_group_assets(id, opts = { read_timeout: 200 })
  asset_ids = []
  endpoint = "/asset_groups/#{id}/assets"
  fetch_all(endpoint, opts) do |asset_id|
    asset_ids << asset_id
  end
  asset_ids
end

#fetch_asset_group_by_name(name) ⇒ Object



61
62
63
64
65
66
67
# File 'lib/domain/asset_group/api.rb', line 61

def fetch_asset_group_by_name(name)
  fetch_all('/asset_groups', name:) do |resource|
    asset_group = AssetGroup.from_json(resource)
    return asset_group if asset_group.name == name
  end
  nil
end

#fetch_asset_groups(opts = {}) ⇒ Object



7
8
9
10
11
12
13
# File 'lib/domain/asset_group/api.rb', line 7

def fetch_asset_groups(opts = {})
  puts 'fetching asset groups'
  fetch_all('/asset_groups', opts) do |resource|
    puts resource
    yield AssetGroup.from_json(resource)
  end
end

#fetch_country_cyberark(country) ⇒ Object

TODO: use directly country code 2024-04-25 08:53



15
16
17
18
# File 'lib/domain/shared_credential/api.rb', line 15

def fetch_country_cyberark(country)
  credentials = all_shared_credentials
  credentials.find { |credential| credential.cyberark?(country) }
end

#fetch_country_scan_engine_pools(country) ⇒ Object

return scan engines that went from up to down given the previous status in a hash if the previous status is not known, assume it was up

TODO fetch pools from API



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/domain/scan_engine/api.rb', line 66

def fetch_country_scan_engine_pools(country)
  pools = [
    { "id": 31, "name": 'DRC Core Network Scanners', "engines": [32, 145],
      "sites": [640, 258, 1222, 70, 904, 1097, 1225, 78, 79, 1040, 1107, 406, 920, 795, 412, 797, 1185, 1058, 1188, 997, 1192, 1193, 1194, 491, 749, 750, 942, 751, 752, 753, 562, 1144] },
    { "id": 19, "name": 'Ghana Core Network Scanners', "engines": [16, 22],
      "sites": [1220, 133, 197, 198, 199, 1227, 1163, 463, 1039, 80, 145, 405, 1111, 984, 920, 414, 990, 802, 99, 803, 1190, 551, 999, 1127, 234, 1066, 1195, 492, 1196, 1197, 1198, 942, 1199, 560, 1072, 1010, 1014, 119, 760, 761, 1017, 762, 1082, 763, 764, 1023] },
    { "id": 88, "name": 'Jersey Core Network Scanners', "engines": [147],
      "sites": [843, 844, 1164, 845, 1165, 846, 255] },
    { "id": 59, "name": 'Mozambique Core Network Scanners', "engines": [113, 97],
      "sites": [132, 263, 1099, 1163, 721, 785, 1233, 786, 147, 787, 84, 788, 789, 790, 920, 409, 1249, 1251, 421, 1253, 1254, 103, 1255, 1260, 942, 1199, 819, 820, 950, 954] },
    { "id": 41, "name": 'Zimbabwe Core Network Scanners', "engines": [40],
      "sites": [64, 837, 838, 839, 1095, 1287, 1288, 841, 1289, 842, 1290, 1291, 1163, 1292, 144, 1173, 1241, 990, 1002, 428, 110, 116, 120, 1082, 959] },
    { "id": 18, "name": 'Malawi Core Network Scanners', "engines": [92],
      "sites": [128, 960, 261, 903, 136, 1032, 778, 779, 971, 1163, 780, 781, 1231, 464, 146, 83, 1171, 920, 1305, 1052, 1054, 990, 992, 1248, 609, 418, 1060, 869, 1253, 102, 1000, 1128, 1256, 1065, 1257, 1258, 1259, 1132, 942, 815, 816, 883, 1011, 887, 1082] },
    { "id": 77, "name": 'Uganda Core Network Scanners', "engines": [134, 149, 69],
      "sites": [1025, 266, 459, 142, 1038, 720, 1296, 852, 470, 599, 1239, 152, 1112, 920, 857, 858, 731, 1307, 1308, 1053, 1309, 1310, 990, 1311, 1056, 1312, 296, 108, 300, 1069, 494, 942, 1199, 561, 821, 439, 824, 1016, 953, 826, 1018, 124, 828, 1020, 829] },
    { "id": 29, "name": 'Mauritius Core Network Scanners', "engines": [93],
      "sites": [4, 5, 6, 262, 1159, 9, 137, 1163, 1101, 782, 783, 1295, 784, 1232, 1170, 1302, 1303, 920, 1305, 1306, 990, 419, 430, 1199, 817, 818, 1082] },
    { "id": 127, "name": 'International (London, Beijing...) Core Network Scanners', "engines": [121],
      "sites": [912, 305, 854, 855, 444, 479] },
    { "id": 52, "name": 'Angola Core Network Scanners', "engines": [95],
      "sites": [1163, 1298, 531, 1299, 22, 1174, 23, 1175, 408, 920, 1049, 1177, 26, 410, 1178, 27, 1179, 1180, 1311, 544, 942, 1199, 1077, 1082, 1223, 327, 328, 1096, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 990, 735, 736, 737, 738, 739, 740, 996, 741, 490, 634] },
    { "id": 36, "name": 'Zambia Core Network Scanners', "engines": [79],
      "sites": [1280, 513, 1281, 1283, 902, 1163, 143, 1041, 920, 1050, 925, 547, 1059, 420, 1064, 427, 942, 945, 1076, 181, 63, 831, 832, 833, 834, 835, 1091, 836, 967, 201, 850, 1108, 853, 1240, 474, 986, 606, 994, 1001, 364, 109, 114, 243, 885, 886, 122, 1274, 1277, 1278, 1279] },
    { "id": 72, "name": 'Lesotho Core Network Scanners', "engines": [70],
      "sites": [962, 260, 773, 1093, 774, 135, 775, 776, 777, 461, 1230, 655, 82, 607, 417, 101, 487, 811, 812, 1202, 1208, 1209, 1210, 1211, 127] },
    { "id": 26, "name": 'Swaziland Core Network Scanners', "engines": [20],
      "sites": [129, 1094, 139, 1163, 460, 1294, 1237, 150, 920, 798, 990, 799, 1315, 1316, 998, 1318, 871, 1319, 424, 106, 876, 942, 1199, 112, 241, 754, 755, 947, 756, 884, 757, 758, 759, 1079, 1082] },
    { "id": 26, "name": 'Eswatini Core Network Scanners', "engines": [20],
      "sites": [129, 1094, 139, 1163, 460, 1294, 1237, 150, 920, 798, 990, 799, 1315, 1316, 998, 1318, 871, 1319, 424, 106, 876, 942, 1199, 112, 241, 754, 755, 947, 756, 884, 757, 758, 759, 1079, 1082] },
    { "id": 48, "name": 'Kenya Core Network Scanners', "engines": [106],
      "sites": [768, 769, 514, 770, 771, 772, 1092, 1221, 1029, 966, 1160, 1161, 651, 1229, 654, 81, 1105, 1043, 404, 920, 1243, 1244, 284, 1245, 1118, 1246, 287, 416, 288, 289, 545, 100, 806, 807, 873, 1129, 1004, 238, 1071, 564, 1141, 952, 505, 253, 126] },
    { "id": 76, "name": 'Botswana Core Network Scanners', "engines": [75, 74],
      "sites": [1028, 1224, 972, 1100, 983, 920, 665, 1113, 411, 987, 1051, 1181, 991, 1183, 1184, 1187, 37, 1061, 1189, 742, 870, 39, 743, 744, 872, 745, 746, 43, 747, 875, 1003, 44, 748, 45, 237, 877, 944, 882, 1075] },
    { "id": 73, "name": 'Ivory Coast Core Network Scanners', "engines": [146],
      "sites": [259, 804, 805, 1098, 1228, 141, 1200, 1201, 1203, 1172, 1204, 117, 1206, 407, 1207, 282, 765, 766, 415, 767] },
    { "id": 84, "name": 'Nigeria Core Network Scanners', "engines": [82, 83],
      "sites": [800, 801, 130, 1157, 423, 264, 105, 1102, 942, 847, 1263, 848, 432, 1265, 946, 1235, 1267, 1268, 149, 86, 920, 1273, 1086] },
    { "id": 12, "name": 'South Africa Core Network Scanners', "engines": [8, 14, 9, 13, 91, 11, 7, 6],
      "sites": [515, 516, 517, 520, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535, 537, 539, 540, 541, 286, 542, 543, 548, 549, 550, 1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 48, 304, 1328, 1329, 1330, 307, 1331, 1332, 1333, 1334, 567, 1335, 568, 570, 571, 572, 573, 574, 576, 65, 577, 578, 66, 579, 583, 74, 587, 594, 597, 598, 1110, 87, 600, 601, 604, 349, 353, 610, 612, 613, 615, 360, 616, 617, 618, 620, 1135, 624, 1138, 1139, 1140, 629, 630, 632, 633, 642, 1156, 1162, 1163, 1166, 658, 662, 663, 664, 920, 666, 667, 668, 669, 413, 670, 671, 927, 673, 675, 676, 677, 678, 679, 169, 681, 682, 683, 684, 686, 942, 433, 689, 690, 436, 692, 440, 441, 186, 187, 699, 188, 1212, 1213, 189, 190, 703, 1215, 704, 1216, 1217, 195, 196, 205, 206, 465, 212, 213, 216, 728, 729, 218, 730, 219, 220, 222, 478, 990, 1247, 480, 228, 229, 230, 231, 488, 235, 242, 500, 245, 504, 506, 507, 509, 511] },
    { "id": 123, "name": 'Namibia Core Network Scanners', "engines": [122],
      "sites": [1089, 131, 585, 586, 1037, 657, 1234, 148, 85, 791, 792, 793, 538, 989, 422, 1062, 104, 552, 1005, 1261, 1266, 948, 1012, 822, 1270, 823, 1271, 1272, 639] },
    { "id": 39, "name": 'Tanzania Core Network Scanners', "engines": [38, 44],
      "sites": [384, 1024, 1026, 1282, 1284, 1285, 1286, 265, 395, 652, 462, 849, 851, 659, 596, 1238, 151, 985, 608, 808, 425, 809, 810, 107, 1067, 493, 1006, 113, 1009, 1015, 379, 1019, 1275, 380, 61, 125, 381, 382, 510, 1022, 383, 1087] }
  ]
  pools.find { |pool| pool[:name].downcase.include?(country.downcase) }
end

#fetch_discovery_scan_template_id(country) ⇒ Object



194
195
196
197
198
199
200
# File 'lib/domain/site/api.rb', line 194

def fetch_discovery_scan_template_id(country)
  if country == 'South Africa'
    settings[:za_discovery_template_id]
  else
    settings[:ar_discovery_template_id]
  end
end

#fetch_domain_cyberark(domains = []) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/domain/shared_credential/api.rb', line 39

def fetch_domain_cyberark(domains = [])
  credentials = Set.new
  domains.map(&:downcase)
         .each do |domain|
    if domain.include?('eswitch.sbicdirectory.com')
      credentials.add 'CyberArk South Africa SBICZA01'
      credentials.add 'CyberArk South Africa ESWITCH'
    elsif domain.include?('branches.sbicdirectory.com')
      credentials.add 'CyberArk South Africa SBICZA01'
    elsif domain.include?('scmbdicdirectory.com')
      credentials.add 'CyberArk South Africa SBICZA01'
    elsif domain.include?('za.sbicdirectory')
      credentials.add 'CyberArk South Africa SBICZA01'
    elsif domain.include?('stanlibdirectory.com')
      credentials.add 'CyberArk South Africa STANLIB'
    end
  end
  # puts "Credentials #{credentials}"
  return [] if credentials.empty?

  all_shared_credentials.select do |shared_credential|
    credentials.include?(shared_credential.name)
  end
end

#fetch_microsoft_product_lifecycle(spreadsheet, tabsheet_name: 'lifecycle_data') ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/domain/software/api.rb', line 10

def fetch_microsoft_product_lifecycle(spreadsheet, tabsheet_name: 'lifecycle_data')
  xlsx = Roo::Excelx.new(spreadsheet)
  sheet = xlsx.sheet(tabsheet_name)
  headers = xlsx.row(7)
  p headers
  models = []
  total_rows = sheet.last_row - 7
  progress_bar = ProgressBar.create(
    title: "Processing #{spreadsheet}",
    total: total_rows,
    format: '%a %B %p%% %t'
  )
  xlsx.each_row_streaming(offset: 7) do |row|
    values = row.map(&:value)
    attributes = Hash[headers.zip(values)]
    models << MicrosoftProductLifecycle.new(attributes)

    progress_bar.increment
  end
  models
end

#fetch_report(report_id) ⇒ Object



12
13
14
15
16
17
# File 'lib/domain/report/api.rb', line 12

def fetch_report(report_id)
  fetch("/reports/#{report_id}") do |data|
    return Report.from_json(data)
  end
  nil
end

#fetch_report_by_name(name) ⇒ Object



19
20
21
22
23
24
# File 'lib/domain/report/api.rb', line 19

def fetch_report_by_name(name)
  fetch_reports do |report|
    return report if report.name.downcase == name.downcase
  end
  nil
end

#fetch_report_cyberark_credentials(report) ⇒ Object



298
299
300
301
302
303
304
305
306
307
# File 'lib/domain/report/api.rb', line 298

def fetch_report_cyberark_credentials(report)
  country_code = report.country_code
  if country_code != 'za'
    country = find_by_country_code report.country_code
    Array(fetch_country_cyberark(country.name))
  else
    domains = fetch_report_domains(report)
    fetch_domain_cyberark(domains)
  end
end

#fetch_report_domains(report) ⇒ Object



309
310
311
# File 'lib/domain/report/api.rb', line 309

def fetch_report_domains(report)
  fetch_report_target_domains(report.id)
end

#fetch_report_shared_credentials(report_id:) ⇒ Object



291
292
293
294
295
296
# File 'lib/domain/report/api.rb', line 291

def fetch_report_shared_credentials(report_id:)
  report = fetch_report(report_id)
  raise "Report #{report_id} not found" if report.nil?

  fetch_report_cyberark_credentials(report)
end

#fetch_reportsObject



6
7
8
9
10
# File 'lib/domain/report/api.rb', line 6

def fetch_reports
  fetch_all('/reports') do |resource|
    yield Report.from_json(resource)
  end
end

#fetch_scan_engine_poolsObject



7
8
9
10
11
# File 'lib/domain/scan_engine/api.rb', line 7

def fetch_scan_engine_pools
  fetch_all('/scan_engine_pools') do |resource|
    yield ScanEnginePool.from_json(resource)
  end
end

#fetch_scan_enginesObject



13
14
15
16
17
# File 'lib/domain/scan_engine/api.rb', line 13

def fetch_scan_engines
  fetch_all('/scan_engines') do |resource|
    yield ScanEngine.from_json(resource)
  end
end

#fetch_shared_credential(id) ⇒ Object



83
84
85
86
87
# File 'lib/domain/shared_credential/api.rb', line 83

def fetch_shared_credential(id)
  fetch("/shared_credentials/#{id}") do |data|
    SharedCredential.from_json(data)
  end
end

#fetch_shared_credentialsObject



77
78
79
80
81
# File 'lib/domain/shared_credential/api.rb', line 77

def fetch_shared_credentials
  fetch_all('/shared_credentials') do |resource|
    yield SharedCredential.from_json(resource)
  end
end

#fetch_site(site_id) ⇒ Object



21
22
23
24
25
26
# File 'lib/domain/site/api.rb', line 21

def fetch_site(site_id)
  fetch("/sites/#{site_id}") do |data|
    return Site.from_json(data)
  end
  nil
end

#fetch_site_by_name(name) ⇒ Object



28
29
30
31
32
33
# File 'lib/domain/site/api.rb', line 28

def fetch_site_by_name(name)
  fetch_sites do |site|
    return site if site.name.downcase == name.downcase
  end
  nil
end

#fetch_site_cyberark_credentials(site) ⇒ Object



333
334
335
336
337
338
339
340
341
342
# File 'lib/domain/site/api.rb', line 333

def fetch_site_cyberark_credentials(site)
  country_code = site.country_code
  if country_code != 'za'
    country = find_by_country_code site.country_code
    Array(fetch_country_cyberark(country.name))
  else
    domains = fetch_site_domains(site)
    fetch_domain_cyberark(domains)
  end
end

#fetch_site_domains(site) ⇒ Object



344
345
346
# File 'lib/domain/site/api.rb', line 344

def fetch_site_domains(site)
  fetch_site_target_domains(site.id)
end

#fetch_site_excluded_targets(site_id) ⇒ Object



14
15
16
# File 'lib/domain/site_target/api.rb', line 14

def fetch_site_excluded_targets(site_id)
  fetch_site_targets(site_id, included: false)
end

#fetch_site_included_targets(site_id) ⇒ Object



10
11
12
# File 'lib/domain/site_target/api.rb', line 10

def fetch_site_included_targets(site_id)
  fetch_site_targets(site_id, included: true)
end

#fetch_site_scan_schedules(site_id:) ⇒ Object



7
8
9
10
11
# File 'lib/domain/scan_schedule/api.rb', line 7

def fetch_site_scan_schedules(site_id:)
  fetch_all("/sites/#{site_id}/scan_schedules") do |resource|
    yield ScanSchedule.from_json(resource)
  end
end

#fetch_site_shared_credentials(site_id:) ⇒ Object



326
327
328
329
330
331
# File 'lib/domain/site/api.rb', line 326

def fetch_site_shared_credentials(site_id:)
  site = fetch_site(site_id)
  raise "Site #{site_id} not found" if site.nil?

  fetch_site_cyberark_credentials(site)
end

#fetch_site_target_domains(site_id) ⇒ Object



25
26
27
28
29
30
31
32
33
# File 'lib/domain/site_target/api.rb', line 25

def fetch_site_target_domains(site_id)
  targets = fetch_site_included_targets(site_id)
  hosts = targets.select { |target| target.type == 'host' }
  # remove the host part of the fqdns
  hosts.each_with_object(Set.new) do |host, accu|
    domain = remove_first_part(host.target)
    accu.add domain unless domain.nil? || domain.blank?
  end
end

#fetch_sitesObject



15
16
17
18
19
# File 'lib/domain/site/api.rb', line 15

def fetch_sites
  fetch_all('/sites') do |resource|
    yield Site.from_json(resource)
  end
end

#fetch_tag(site_id) ⇒ Object



75
76
77
78
79
80
81
# File 'lib/domain/tag/api.rb', line 75

def fetch_tag(site_id)
  result = nil
  fetch("/tags/#{site_id}") do |data|
    result = Tag.from_json(data)
  end
  result
end

#fetch_tag_by_name(name) ⇒ Object



49
50
51
52
53
54
55
# File 'lib/domain/tag/api.rb', line 49

def fetch_tag_by_name(name)
  fetch_all('/tags', { name: }) do |resource|
    tag = Tag.from_json(resource)
    return tag if tag.name == name
  end
  nil
end

#fetch_tagsObject



6
7
8
9
10
# File 'lib/domain/tag/api.rb', line 6

def fetch_tags
  fetch_all('/tags') do |resource|
    yield Tag.from_json(resource)
  end
end

#fetch_utr_reportsObject



26
27
28
29
30
31
32
# File 'lib/domain/report/api.rb', line 26

def fetch_utr_reports
  reports = []
  fetch_reports do |report|
    reports << report if report.utr?
  end
  reports
end

#fetch_utr_sitesObject



35
36
37
38
39
40
41
# File 'lib/domain/site/api.rb', line 35

def fetch_utr_sites
  sites = []
  fetch_sites do |site|
    sites << site if site.utr?
  end
  sites
end

#fetch_utr_vulnerabilitiesObject



28
29
30
31
32
33
34
# File 'lib/domain/vulnerability/api.rb', line 28

def fetch_utr_vulnerabilities
  vulnerabilities = []
  fetch_vulnerabilities do |vulnerability|
    vulnerabilities << vulnerability if vulnerability.utr?
  end
  vulnerabilities
end

#fetch_vulnerabilitiesObject



8
9
10
11
12
# File 'lib/domain/vulnerability/api.rb', line 8

def fetch_vulnerabilities
  fetch_all('/vulnerabilities') do |resource|
    yield Vulnerability.from_json(resource)
  end
end

#fetch_vulnerability(site_id) ⇒ Object



14
15
16
17
18
19
# File 'lib/domain/vulnerability/api.rb', line 14

def fetch_vulnerability(site_id)
  fetch("/vulnerabilities/#{site_id}") do |data|
    return Vulnerability.from_json(data)
  end
  nil
end

#fetch_vulnerability_by_name(name) ⇒ Object



21
22
23
24
25
26
# File 'lib/domain/vulnerability/api.rb', line 21

def fetch_vulnerability_by_name(name)
  fetch_vulnerabilities do |vulnerability|
    return vulnerability if vulnerability.name.downcase == name.downcase
  end
  nil
end

#find_by_country_code(cc) ⇒ Object



9
10
11
# File 'lib/domain/country/api.rb', line 9

def find_by_country_code(cc)
  App.db.countries.find { |country| country.code == cc }
end

#get_or_create_asset_group(name:, cached_asset_groups: {}) ⇒ Object

Given a asset_group name return asset_group in the cache if not found in cache,

update the cache
return the asset_group if the cache contains the name
create the custom asset_group with name
return asset_group


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/domain/asset_group/api.rb', line 39

def get_or_create_asset_group(name:, cached_asset_groups: {})
  # Check if the asset_group name already exists in the cache
  from_cache = cached_asset_groups[name]
  return from_cache unless from_cache.nil?

  # If the asset_group is not found in the cache,
  # fetch asset_groups from the API
  from_api = fetch_asset_group_by_name(name)
  unless from_api.nil?
    cached_asset_groups[from_api.name] = from_api
    return from_api
  end

  # create the asset_group and update the cache
  id = create_asset_group(name:)
  return nil if id.nil?

  asset_group = fetch_asset_group(id)
  cached_asset_groups[name] = asset_group
  asset_group
end

#get_or_create_tag(name:, cached_tags: {}) ⇒ Object

Given a tag name return tag in the cache if not found in cache,

update the cache
return the tag if the cache contains the name
create the custom tag with name
return tag


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/domain/tag/api.rb', line 27

def get_or_create_tag(name:, cached_tags: {})
  # Check if the tag name already exists in the cache
  from_cache = cached_tags[name]
  return from_cache unless from_cache.nil?

  # If the tag is not found in the cache,
  # fetch tags from the API
  from_api = fetch_tag_by_name(name)
  unless from_api.nil?
    cached_tags[from_api.name] = from_api
    return from_api
  end

  # create the tag and update the cache
  id = create_tag(name:)
  return nil if id.nil?

  tag = fetch_tag(id)
  cached_tags[name] = tag
  tag
end

#list_shared_credential_utr_sites(credential, utr_sites) ⇒ Object



20
21
22
23
# File 'lib/domain/shared_credential/api.rb', line 20

def list_shared_credential_utr_sites(credential, utr_sites)
  site_ids = credential.sites
  utr_sites.select { |site| site_ids.include?(site.id) }
end

#remove_shared_credential_sites(credential, site_ids = []) ⇒ Object



30
31
32
33
34
35
36
37
# File 'lib/domain/shared_credential/api.rb', line 30

def remove_shared_credential_sites(credential, site_ids = [])
  to_be_removed = credential.sites & site_ids
  return if to_be_removed.empty?

  endpoint = "/shared_credentials/#{credential.id}"
  credential.sites -= to_be_removed
  put(endpoint, credential)
end

#remove_shared_credential_utr_sites(credential, utr_sites) ⇒ Object



25
26
27
28
# File 'lib/domain/shared_credential/api.rb', line 25

def remove_shared_credential_utr_sites(credential, utr_sites)
  site_ids = utr_sites.select(&:utr?).map(&:id)
  remove_shared_credential_sites(credential, site_ids)
end

#scan_engine_poolsObject



13
14
15
# File 'lib/domain/scan_engine_pool/api.rb', line 13

def scan_engine_pools
  @scan_engine_pools ||= _fetch_all_scan_engines_pools
end

#scan_engines_from_up_to_down(engines, previous_status) ⇒ Object

return scan engines that went from up to down given the previous status in a hash if the previous status is not known, assume it was up



31
32
33
34
35
36
37
# File 'lib/domain/scan_engine/api.rb', line 31

def scan_engines_from_up_to_down(engines, previous_status)
  downs = engines.select(&:down?).reject(&:rapid7_hosted?)
  downs.select do |site|
    status = previous_status[site.id]
    status.nil? ? true : status
  end
end

#scan_slot(utr_digits) ⇒ Object

return day of week and starts Time of scan ScanSchedule given the UTR digits



134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/domain/site/api.rb', line 134

def scan_slot(utr_digits)
  index = utr_digits % 25
  day = index / 5
  hour = index % 5

  days = %w[monday tuesday friday saturday sunday]
  hours = [20, 22, 0, 2, 4]

  {
    day_of_week: days[day],
    start_time: hours[hour]
  }
end

#settingsObject



7
8
9
10
11
12
13
# File 'lib/domain/site/api.rb', line 7

def settings
  {
    ar_discovery_template_id: ENV['AR_DISCOVERY_TEMPLATE_ID'],
    za_discovery_template_id: ENV['ZA_DISCOVERY_TEMPLATE_ID'],
    za_deep_dive_scanners: ENV['ZA_DEEP_DIVE_SCANNERS']
  }
end

#starts_discovery_scan(vulnerability_id:) ⇒ Object



275
276
277
278
279
280
281
# File 'lib/domain/site/api.rb', line 275

def starts_discovery_scan(site_id:)
  site = fetch_site(site_id)
  raise "Site ##{siteId} does not exist." if site.nil?

  params = {}
  post("/sites/#{site_id}/scans", params)
end

#toggle_utr_site_schedule(site:, enabled:) ⇒ Object



163
164
165
166
167
168
169
170
171
# File 'lib/domain/site/api.rb', line 163

def toggle_utr_site_schedule(site:, enabled:)
  site_id = site.id
  fetch_site_scan_schedules(site_id:) do |scan_schedule|
    next if scan_schedule.enabled == enabled

    scan_schedule.enabled = enabled
    update_scan_schedule(scan_schedule, site_id:)
  end
end

#update_report_owner(report, owner_id) ⇒ Object



51
52
53
54
55
56
57
58
59
60
# File 'lib/domain/report/api.rb', line 51

def update_report_owner(report, owner_id)
  payload = report.to_hash.merge({ 'owner' => owner_id })
  payload.delete('id')
  payload.delete_if { |_k, v| v.nil? }
  # frequency = payload['frequency']
  # frequency.delete('nextRuntimes')
  # payload.merge { 'frequency' => frequency }
  endpoint = "/reports/#{report.id}"
  put(endpoint, payload)
end

#update_scan_schedule(scan_schedule, site_id:) ⇒ Object



60
61
62
63
64
# File 'lib/domain/scan_schedule/api.rb', line 60

def update_scan_schedule(scan_schedule, site_id:)
  id = scan_schedule.id
  endpoint = "/sites/#{site_id}/scan_schedules/#{id}"
  put(endpoint, scan_schedule)
end

#update_shared_credential_sites(credential:, site_ids: []) ⇒ Object

Add the site_id to the shared credential sites



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/domain/shared_credential/api.rb', line 65

def update_shared_credential_sites(credential:, site_ids: [])
  new_site_ids = site_ids - credential.sites
  if new_site_ids.empty?
    puts "#{credential.name} contains already sites #{site_ids}"
    return
  end

  credential.sites += new_site_ids
  endpoint = "/shared_credentials/#{credential.id}"
  put(endpoint, credential)
end

#update_xxx_report_owner(owner_id) ⇒ Object



62
63
64
65
66
67
68
69
70
71
# File 'lib/domain/report/api.rb', line 62

def update_xxx_report_owner(owner_id)
  fetch_reports do |report|
    next unless report.name.starts_with?('xxx')
    next if report.owner == owner_id

    report.timezone = 'Africa/Harare'
    puts "Change #{report.name} owner from #{report.owner}"
    update_report_owner(report, owner_id)
  end
end

#upsert_site_shared_credentials(site_id:) ⇒ Object



252
253
254
255
256
257
258
# File 'lib/domain/site/api.rb', line 252

def upsert_site_shared_credentials(site_id:)
  site = fetch_site(site_id)
  raise "Site #{site_id} does not exist." if site.nil?
  raise "Site #{site.name} is not a UTR site" unless site.utr?

  add_shared_credentials(site)
end

#upsert_utr_report_schedule(report_id:) ⇒ Object



167
168
169
170
171
172
173
174
# File 'lib/domain/report/api.rb', line 167

def upsert_utr_report_schedule(report_id:)
  report = fetch_report(report_id)
  raise 'The report is not a valid UTR report' unless report.utr?

  delete_utr_report_schedules(report_id:)

  create_utr_report_schedule(report_id:)
end

#upsert_utr_site_schedule(site_id:) ⇒ Object



154
155
156
157
158
159
160
161
# File 'lib/domain/site/api.rb', line 154

def upsert_utr_site_schedule(site_id:)
  site = fetch_site(site_id)
  raise 'The site is not a valid UTR site' unless site.utr?

  delete_utr_site_schedules(site_id:)

  create_utr_site_schedule(site_id:)
end