Class: InsightVMApi

Inherits:
Object
  • Object
show all
Defined in:
lib/service/api/insightvm.rb,
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/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/scan_template/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

Overview

InsightVMApi class provides an interface for interacting with the InsightVM API. It handles request building, and response parsing for various API endpoints.

Examples:

Initializing the API client

api = InsightVMApi.new(
  base_url: 'https://your-insightvm-instance.com',
  username: 'your_username',
  password: 'your_password'
)

Fetching all sites

api.fetch_all('/sites') do |site|
  puts site['name']
end

Getting a specific site

site = api.get('/sites/1')
puts site.inspect

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_url:, username:, password:) ⇒ InsightVMApi

Initializes a new InsightVMApi instance.

Parameters:

  • base_url (String)

    The base URL for the InsightVM API

  • username (String)

    The username for API authentication

  • password (String)

    The password for API authentication



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/service/api/insightvm.rb', line 37

def initialize(base_url:, username:, password:)
  @base_url = base_url
  @base_auth = ['Basic', Base64.strict_encode64("#{username}:#{password}")].join(' ')
  @options = {
    headers: {
      'Accept' => 'application/json',
      'Content-Type' => 'application/json',
      'Authorization' => @base_auth
    }
  }
  @cached_tags = nil
  @shared_credentials = nil
end

Instance Attribute Details

#base_authString (readonly)

The Basic Authentication string used for requests

Returns:

  • (String)

    the current value of base_auth



29
30
31
# File 'lib/service/api/insightvm.rb', line 29

def base_auth
  @base_auth
end

#base_urlString (readonly)

The base URL for the InsightVM API

Returns:

  • (String)

    the current value of base_url



29
30
31
# File 'lib/service/api/insightvm.rb', line 29

def base_url
  @base_url
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 |site|
    @scan_engine_pools << site
  end
  @scan_engine_pools
end

#add_shared_credentials(site) ⇒ Object



494
495
496
497
498
499
500
501
# File 'lib/domain/site/api.rb', line 494

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_site_shared_credentials(cmdb_site_id:, country_discovery_site_id:) ⇒ Object



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/domain/site/api.rb', line 418

def add_site_shared_credentials(
  cmdb_site_id:,
  country_discovery_site_id:
)
  credentials = fetch_assigned_shared_credentials(
    site_id: country_discovery_site_id
  )
  site_ids = [cmdb_site_id]
  credentials.each do |credential|
    update_shared_credential_sites(
      credential:,
      site_ids:
    )
  end
end

#add_site_tags(site_id:, tag_names:) ⇒ Object



408
409
410
411
412
413
414
415
416
# File 'lib/domain/site/api.rb', line 408

def add_site_tags(site_id:, tag_names:)
  tags = tag_names.map do |tag_name|
    upsert_tag(name: tag_name)
  end
  return nil if tags.nil?

  tag_ids = tags.map(&:id)
  add_tags_to_site(site_id:, tag_ids:)
end

#add_tags_to_site(site_id:, tag_ids:) ⇒ Object



79
80
81
82
83
# File 'lib/domain/tag/api.rb', line 79

def add_tags_to_site(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



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

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_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

#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



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

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



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

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

#create_cmdb_discovery_site(discovery_site:, starts_discovery: true) ⇒ Object



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/domain/site/api.rb', line 374

def create_cmdb_discovery_site(
  discovery_site:,
  starts_discovery: true
)
  site_id = create_utr_site(
    name: discovery_site.name,
    description: discovery_site.name,
    targets: discovery_site.included_targets,
    engine_id: discovery_site.scan_engine,
    scan_template_id: discovery_site.scan_template
  )

  if site_id.nil?
    puts "#{discovery_site.name} site was not created"
    return
  end
  add_site_shared_credentials(
    cmdb_site_id: site_id,
    country_discovery_site_id: discovery_site.country_discovery_site_id
  )
  add_site_tags(
    site_id:,
    tag_names: discovery_site.tag_names
  )
  starts_discovery_scan(site_id) if starts_discovery
  site_id
rescue StandardError => e
  puts "Error occured while creating site: #{e.message}"
  puts e.backtrace.join("\n")
  puts "Delete partially created #{discovery_site.name}"
  delete_site(site_id) unless site_id.nil?
  nil
end

#create_cmdb_vulnerability_scan_site_schedule(site:, vulnerability_scan_site:, enable_schedule: false) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/domain/site/api.rb', line 241

def create_cmdb_vulnerability_scan_site_schedule(
  site:,
  vulnerability_scan_site:,
  enable_schedule: false
)
  start_hour = vulnerability_scan_site.start_hour.to_i
  now = DateTime.now
  start_time = DateTime.new(
    now.year,
    now.month,
    now.day,
    start_hour
  )
  local_time = Service::DateTime.closest_day_of_week(
    start_time,
    vulnerability_scan_site.start_day
  )
  start = Service::TimeZone.iso8601(
    country_code: vulnerability_scan_site.country_code,
    local_time:
  )
  repeat = ScanSchedule::Repeat.new(
    every: 'week',
    day_of_week: vulnerability_scan_site.start_day,
    interval: 1
  )
  create_scan_schedule(
    site_id: site.id,
    duration: "PT#{vulnerability_scan_site.duration_in_hours}H",
    scan_engine_id: site.scan_engine,
    scan_template_id: site.scan_template,
    repeat:,
    start:,
    scan_name: site.name,
    enabled: enable_schedule
  )
end

#create_cmdb_vulnerability_site(vulnerability_scan_site:, enable_schedule: false) ⇒ Object



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/domain/site/api.rb', line 332

def create_cmdb_vulnerability_site(
  vulnerability_scan_site:,
  enable_schedule: false
)
  puts "\t Create asset group"
  asset_group_id = fetch_or_create_asset_group_for_cmdb_vulnerability_scan(vulnerability_scan_site)
  raise "#{vulnerability_site.asset_group_name} asset group not found" if asset_group_id.nil?

  puts "\t Create site"
  country_code = vulnerability_scan_site.country_code
  scan_template = fetch_vulnerability_scan_template(country_code)
  site_id = create_site(
    name: vulnerability_scan_site.name,
    description: vulnerability_scan_site.name,
    importance: 'high',
    included_asset_group_ids: [asset_group_id],
    engine_id: vulnerability_scan_site.scan_engine_pool_id,
    scan_template_id: scan_template.id
  )
  raise "#{vulnerability_site.name} site was not created" if site_id.nil?

  puts "\t Assign shared credentials"
  add_site_shared_credentials(
    cmdb_site_id: site_id,
    country_discovery_site_id: vulnerability_scan_site.country_discovery_site_id
  )

  puts "\t Create schedule"
  site = fetch_site(site_id)
  create_cmdb_vulnerability_scan_site_schedule(
    site:,
    vulnerability_scan_site:,
    enable_schedule:
  )
  site_id
rescue StandardError => e
  App.backtrace(e)
  puts "\t Delete partially created #{vulnerability_scan_site.name}"
  delete_site(site_id) unless site_id.nil?
  nil
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



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
# File 'lib/domain/report/api.rb', line 69

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



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
# File 'lib/domain/scan_schedule/api.rb', line 68

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(options = {}) ⇒ String?

Creates a new site with the given parameters

Parameters:

  • options (Hash) (defaults to: {})

    Options for site creation

Options Hash (options):

  • :name (String)

    The name of the site (required)

  • :description (String)

    The description of the site (required)

  • :engine_id (String)

    The ID of the scan engine to use (required)

  • :scan_template_id (String)

    The ID of the scan template to use (required)

  • :importance (String) — default: 'normal'

    The importance of the site

  • :included_targets (Array<String>) — default: []

    Targets to include in the scan

  • :excluded_targets (Array<String>) — default: []

    Targets to exclude from the scan

  • :included_asset_group_ids (Array<String>) — default: []

    Asset group IDs to include

  • :excluded_asset_group_ids (Array<String>) — default: []

    Asset group IDs to exclude

Returns:

  • (String, nil)

    The ID of the created site, or nil if creation failed

Raises:

  • (ArgumentError)

    If any required option is missing



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/domain/site/api.rb', line 123

def create_site(options = {})
  default_options = {
    importance: 'normal',
    included_targets: [],
    excluded_targets: [],
    included_asset_group_ids: [],
    excluded_asset_group_ids: []
  }

  options = default_options.merge(options)

  required_keys = %i[name description engine_id scan_template_id]
  missing_keys = required_keys.select { |key| options[key].nil? }
  raise ArgumentError, "Missing required options: #{missing_keys.join(',')}" if missing_keys.any?

  params = {
    name: options[:name],
    description: options[:description],
    importance: options[:importance],
    engineId: options[:engine_id],
    scanTemplateId: options[:scan_template_id],
    scan: {
      assets: {
        includedTargets: {
          addresses: options[:included_targets]
        },
        excludedTargets: {
          addresses: options[:excluded_targets]
        },
        includedAssetGroups: {
          assetGroupIDs: options[:included_asset_group_ids]
        },
        excludedAssetGroups: {
          assetGroupIDs: options[: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



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/domain/tag/api.rb', line 53

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



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/domain/report/api.rb', line 264

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



196
197
198
199
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
# File 'lib/domain/report/api.rb', line 196

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)
  sitentry_scan_engine_pools(country)
  # puts "Scan engine pool #{scan_engine_pool}"
  engine_id = site[:id]
  scan_template_id = fetch_vulnerability_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}"
    upsert_tag(name: tag_name, cached_tags:)
  end
  tag_ids = tags.map(&:id)

  add_tags_to_site(report_id:, tag_ids:)
  report_id
end

#create_utr_report_schedule(report_id:) ⇒ Object



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

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



108
109
110
111
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
# File 'lib/domain/report/api.rb', line 108

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 "\t Create asset group: #{report_name}"
    create_asset_group_for(report_id:, report_name:)

    puts "\t Schedule the scan"
    # TODO

    next unless starts_discovery

    puts "\t Start discovery scan"
    starts_discovery_scan(report_id:)
  end
end

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



531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
# File 'lib/domain/site/api.rb', line 531

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 Note: only return the onboard assets



436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
# File 'lib/domain/site/api.rb', line 436

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)
  sitentry_scan_engine_pools(country)
  # puts "Scan engine pool #{scan_engine_pool}"
  engine_id = site[:id]
  scan_template_id = fetch_vulnerability_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,  network_zone
  tag_names = assets.first.utr_tag_names
  tags = tag_names.map do |tag_name|
    puts "\tAdd tag: #{tag_name}"
    upsert_tag(name: tag_name)
  end
  tag_ids = tags.map(&:id)

  add_tags_to_site(site_id:, tag_ids:)
  site_id
end

#create_utr_site_schedule(site_id:) ⇒ Object



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/domain/site/api.rb', line 279

def create_utr_site_schedule(site_id:)
  sitee(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

TODO: mark this function for deletion



167
168
169
170
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
# File 'lib/domain/site/api.rb', line 167

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 "\t Create asset group: #{site_name}"
    create_asset_group_for(site_id:, site_name:)

    puts "\t Schedule the scan"
    # TODO

    next unless starts_discovery

    puts "\t Start discovery scan"
    starts_discovery_scan(site_id:)
  end
end

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



90
91
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
# File 'lib/domain/vulnerability/api.rb', line 90

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



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/domain/vulnerability/api.rb', line 237

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



161
162
163
164
165
166
167
168
169
170
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
# File 'lib/domain/vulnerability/api.rb', line 161

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)
  sitentry_scan_engine_pools(country)
  # puts "Scan engine pool #{scan_engine_pool}"
  engine_id = site[: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}"
    upsert_tag(name: tag_name, cached_tags:)
  end
  tag_ids = tags.map(&:id)

  add_tags_to_site(vulnerability_id:, tag_ids:)
  vulnerability_id
end

#create_utr_vulnerability_schedule(vulnerability_id:) ⇒ Object



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

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



51
52
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
# File 'lib/domain/vulnerability/api.rb', line 51

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

TODO: remove this method



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
# File 'lib/domain/scan_schedule/api.rb', line 23

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(endpoint, id, _attempts = 0) ⇒ Object

Performs a DELETE request to the specified endpoint.

Parameters:

  • endpoint (String)

    The base API endpoint

  • id (String, Integer)

    The ID of the resource to delete

  • attempts (Integer)

    The number of delete attempts (for internal use)



102
103
104
105
106
107
108
109
110
# File 'lib/service/api/insightvm.rb', line 102

def delete(endpoint, id, _attempts = 0)
  response = run_request(:delete, "#{endpoint}/#{id}")

  if response.is_a?(Hash) && response[:error]
    puts "Error deleting #{endpoint}/#{id}: #{response[:error]}"
  else
    puts "#{endpoint}/#{id} deleted successfully."
  end
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



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

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.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



251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/domain/report/api.rb', line 251

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 "\t Delete asset group with the same name as the report"
  delete_asset_group_by(name: report.name)

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

#delete_report_by(id:, name:) ⇒ Object



244
245
246
247
248
249
# File 'lib/domain/report/api.rb', line 244

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

  report_id = id || fetch_report_by(name:)&.id
  delete_report(report_id)
end

#delete_scan_schedule(site_id:, schedule_id:) ⇒ Object



58
59
60
# File 'lib/domain/scan_schedule/api.rb', line 58

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

#delete_site(site_id, cascade: true) ⇒ Object



515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/domain/site/api.rb', line 515

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

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

    puts "\t Delete asset group with the same name as the site"
    delete_asset_group_by(name: site.name)

    puts "\t Remove #{site.assets} assets from site"
    delete("/sites/#{site_id}/assets", '')
  end
  delete('/sites', site_id)
end

#delete_site_assets(site_id:) ⇒ Object



41
42
43
44
45
46
47
48
# File 'lib/domain/asset/api.rb', line 41

def delete_site_assets(site_id:)
  asset_ids = []
  fetch_site_assets(site_id:) do |asset|
    puts "Asset ##{asset.id} #{asset.ip} #{asset.host_name}"
    asset_ids << asset.id
  end
  delete_assets(asset_ids)
end

#delete_site_by(id:, name:) ⇒ Object



508
509
510
511
512
513
# File 'lib/domain/site/api.rb', line 508

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

  site_id = id || fetch_site_by_name(name)&.id
  delete_site(site_id) unless site_id.nil?
end

#delete_utr_report_schedules(report_id:) ⇒ Object



157
158
159
160
161
# File 'lib/domain/report/api.rb', line 157

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



216
217
218
219
220
# File 'lib/domain/site/api.rb', line 216

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_sites(confirmation: true) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/domain/site/api.rb', line 86

def delete_utr_sites(confirmation: true)
  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
  prompt = "#{sites.count} sites will be deleted. Are you sure?"
  if confirmation && !confirm_action(prompt)
    puts 'Operation cancelled. No UTR sites were deleted.'
    return
  end

  sites.each do |site|
    next unless site.utr? # double-check

    puts ''
    puts "Deleting site #{site.name}"

    delete_site(site.id)
  end
end

#delete_utr_vulnerabilitiesObject



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

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



222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/domain/vulnerability/api.rb', line 222

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



211
212
213
214
215
216
217
218
219
220
# File 'lib/domain/vulnerability/api.rb', line 211

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(endpoint, attempts = 0) {|response| ... } ⇒ Object

Fetches data from an endpoint with retry logic.

Parameters:

  • endpoint (String)

    The API endpoint to fetch data from

  • attempts (Integer) (defaults to: 0)

    The number of fetch attempts (for internal use)

Yields:

  • (response)

    Gives the successful response to the block

Raises:

  • (RuntimeError)

    If the maximum number of retries is exceeded



141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/service/api/insightvm.rb', line 141

def fetch(endpoint, attempts = 0)
  max_retries = 5
  response = get(endpoint)

  if response.is_a?(Hash) && response[:error]
    raise "Network error after #{max_retries} attempts: #{response[:error]}" unless attempts < max_retries

    sleep 2**attempts
    fetch(endpoint, attempts + 1)

  else
    yield response
  end
end

#fetch_all(endpoint, opts = {}) {|resource| ... } ⇒ Object

Fetches all resources from a paginated endpoint.

Parameters:

  • endpoint (String)

    The API endpoint to fetch resources from

  • opts (Hash) (defaults to: {})

    Additional options for the request (e.g., filters)

Yields:

  • (resource)

    Gives each resource to the block



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/service/api/insightvm.rb', line 56

def fetch_all(endpoint, opts = {}, &block)
  params = {
    page: 0,
    size: 100
  }.merge(opts)

  loop do
    response = get("#{endpoint}?#{URI.encode_www_form(params)}")
    break if response.is_a?(Hash) && response[:error]

    resources = response['resources']
    break if resources.nil?

    resources.each(&block)

    current_page = response['page']&.[]('number') || 0
    pages = response['page']&.[]('totalPages') || 0
    break if params[:page] >= pages || current_page + 1 >= pages

    params[:page] += 1
  end
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



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

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 }, &block) ⇒ Object



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

def fetch_asset_group_assets(id, opts = { read_timeout: 200 }, &block)
  return to_enum(__method__, id, opts) unless block_given?

  endpoint = "/asset_groups/#{id}/assets"

  fetch_all(endpoint, opts, &block)
end

#fetch_asset_group_by_name(name) ⇒ Object



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

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_assigned_shared_credentials(site_id:) ⇒ Object



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

def fetch_assigned_shared_credentials(site_id:)
  shared_credentials.select do |shared_credential|
    shared_credential.assigned_to_all_sites? || shared_credential.sites&.include?(site_id.to_i)
  end
end

#fetch_cmdb_discovery_sitesObject



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

def fetch_cmdb_discovery_sites
  return to_enum(__method__) unless block_given?

  fetch_sites.select(&:cmdb_discovery?).each do |site|
    discovery_site = CountryDiscoverySite.new(
      id: site.id,
      name: site.name,
      scan_engine: site.scan_engine,
      scan_template: site.scan_template
    )
    yield discovery_site
  end
end

#fetch_country_cyberark(country) ⇒ Object

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



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

def fetch_country_cyberark(country)
  shared_credentials.find { |credential| credential.cyberark?(country) }
end

#fetch_country_discovery_sitesObject

fetch_sites.select(&:cmdb_vulnerability?).each do |site|

  discovery_site = CmdbVulnerabilitySite.new(
    id: site.id,
    name: site.name,
    scan_engine: site.scan_engine,
    scan_template: site.scan_template
  )
  yield discovery_site
end

end



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/domain/site/api.rb', line 54

def fetch_country_discovery_sites
  return to_enum(__method__) unless block_given?

  fetch_sites.select(&:country_discovery?).each do |site|
    discovery_site = CountryDiscoverySite.new(
      id: site.id,
      name: site.name,
      scan_engine: site.scan_engine,
      scan_template: site.scan_template
    )
    yield discovery_site
  end
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_domain_cyberark(domains = []) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/domain/shared_credential/api.rb', line 50

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?

  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_or_create_asset_group_for_cmdb_vulnerability_scan(site) ⇒ Object



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

def fetch_or_create_asset_group_for_cmdb_vulnerability_scan(site)
  asset_group = fetch_asset_group_by_name(site.asset_group_name)
  return asset_group.id unless asset_group.nil?

  filters = [site.country_code, site.network_zone, site.business_unit_code].map do |tag|
    {
      field: 'custom-tag',
      operator: 'is',
      value: tag
    }
  end

  create_asset_group(
    name: site.asset_group_name,
    description: "Asset Group for #{site.name}",
    search_criteria: { match: 'all', filters: }
  )
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



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

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



299
300
301
# File 'lib/domain/report/api.rb', line 299

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

#fetch_report_shared_credentials(report_id:) ⇒ Object



281
282
283
284
285
286
# File 'lib/domain/report/api.rb', line 281

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_scan_templatesObject



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

def fetch_scan_templates
  return to_enum(__method__) unless block_given?

  fetch_all('/scan_templates') do |resource|
    yield ScanTemplate.from_json(resource)
  end
end

#fetch_shared_credential(id) ⇒ Object



87
88
89
90
91
# File 'lib/domain/shared_credential/api.rb', line 87

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

#fetch_shared_credentialsObject



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

def fetch_shared_credentials
  return to_enum(__method__) unless block_given?

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

#fetch_site(site_id) ⇒ Object



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

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

#fetch_site_assets(site_id:) ⇒ Object



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

def fetch_site_assets(site_id:)
  fetch_all("/sites/#{site_id}/assets") do |resource|
    yield Asset.from_json(resource)
  end
end

#fetch_site_by_name(name) ⇒ Object



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

def fetch_site_by_name(name)
  fetch_sites.find do |site|
    site.name.downcase == name.downcase
  end
end

#fetch_site_cyberark_credentials(_site) ⇒ Object



555
556
557
558
559
560
561
562
563
564
# File 'lib/domain/site/api.rb', line 555

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

#fetch_site_domains(_site) ⇒ Object



566
567
568
# File 'lib/domain/site/api.rb', line 566

def fetch_site_domains(_site)
  fetch_site_target_domains(scan_engine_pool.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



548
549
550
551
552
553
# File 'lib/domain/site/api.rb', line 548

def fetch_site_shared_credentials(site_id:)
  sitee(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
20
21
# File 'lib/domain/site/api.rb', line 15

def fetch_sites
  return to_enum(__method__) unless block_given?

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

#fetch_tag(site_id) ⇒ Object



71
72
73
74
75
76
77
# File 'lib/domain/tag/api.rb', line 71

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

#fetch_tagsObject



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

def fetch_tags
  return to_enum(__method__) unless block_given?

  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



82
83
84
# File 'lib/domain/site/api.rb', line 82

def fetch_utr_sites
  fetch_sites.select(&:utr?).to_a
end

#fetch_utr_vulnerabilitiesObject



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

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

#fetch_vulnerabilitiesObject



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

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

#fetch_vulnerability(site_id) ⇒ Object



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

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



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

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

#fetch_vulnerability_scan_engine_pool(_site) ⇒ Object



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

def fetch_vulnerability_scan_engine_pool(_site)
  # TODO
  ScanEngine.new({ id: 50 })
end

#fetch_vulnerability_scan_template(country_code) ⇒ Object



29
30
31
32
# File 'lib/domain/scan_template/api.rb', line 29

def fetch_vulnerability_scan_template(country_code)
  name = get_vulnerability_scan_template_name(country_code)
  find_scan_template_by_name(name)
end

#fetch_vulnerability_with_second_step(spreadsheet, tabsheet_name: 'vulnerability_with_second_step') ⇒ Object



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/domain/vulnerability/api.rb', line 254

def fetch_vulnerability_with_second_step(spreadsheet, tabsheet_name: 'vulnerability_with_second_step')
  xlsx = Roo::Excelx.new(spreadsheet)
  sheet = xlsx.sheet(tabsheet_name)
  headers = xlsx.row(1)
  p headers
  models = []
  total_rows = sheet.last_row - 1
  progress_bar = ProgressBar.create(
    title: "Processing #{spreadsheet}",
    total: total_rows,
    format: '%a %B %p%% %t'
  )
  xlsx.each_row_streaming(offset: 1, pad_cells: true) do |row|
    values = row.map { |cell| clean_value(cell&.value) }
    attributes = Hash[headers.zip(values)]
    model = VulnerabilityWithSecondStep.new(attributes)
    models << model unless model.cve_number.nil?

    progress_bar.increment
  end
  models
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

#find_scan_template_by_name(name) ⇒ Object



23
24
25
26
27
# File 'lib/domain/scan_template/api.rb', line 23

def find_scan_template_by_name(name)
  fetch_scan_templates.find do |scan_template|
    scan_template.name.downcase == name.downcase
  end
end

#find_tag_by_name(name) ⇒ Object



45
46
47
48
49
50
51
# File 'lib/domain/tag/api.rb', line 45

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

#get(path, params = {}) ⇒ Hash

Performs a GET request to the specified path.

Parameters:

  • path (String)

    The API path to send the GET request to

  • params (Hash) (defaults to: {})

    Query parameters to include in the request

Returns:

  • (Hash)

    The parsed JSON response or an error hash



84
85
86
# File 'lib/service/api/insightvm.rb', line 84

def get(path, params = {})
  run_request(:get, path, params:)
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


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

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_vulnerability_scan_template_name(country_code) ⇒ Object



300
301
302
303
304
305
306
# File 'lib/domain/site/api.rb', line 300

def get_vulnerability_scan_template_name(country_code)
  if country_code == 'za'
    settings[:za_vulnerability_scan_template_name]
  else
    settings[:ar_vulnerability_scan_template_name]
  end
end

#list_shared_credential_utr_sites(credential, utr_sites) ⇒ Object



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

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

#patch(endpoint, body) ⇒ Object

Performs a PATCH request to the specified endpoint.

Parameters:

  • endpoint (String)

    The API endpoint to send the PATCH request to

  • body (Hash)

    The request body to be sent as JSON



116
117
118
119
120
121
# File 'lib/service/api/insightvm.rb', line 116

def patch(endpoint, body)
  response = run_request(:patch, endpoint, body: body.to_json)
  return unless response.is_a?(Hash) && response[:error]

  puts "Error PATCH #{endpoint}: #{response[:error]}"
end

#post(endpoint, body) ⇒ Hash

Performs a POST request to the specified endpoint.

Parameters:

  • endpoint (String)

    The API endpoint to send the POST request to

  • body (Hash)

    The request body to be sent as JSON

Returns:

  • (Hash)

    The parsed JSON response or an error hash



93
94
95
# File 'lib/service/api/insightvm.rb', line 93

def post(endpoint, body)
  run_request(:post, endpoint, body: body.to_json)
end

#put(endpoint, body) ⇒ Object

Performs a PUT request to the specified endpoint.

Parameters:

  • endpoint (String)

    The API endpoint to send the PUT request to

  • body (Hash)

    The request body to be sent as JSON



127
128
129
130
131
132
133
# File 'lib/service/api/insightvm.rb', line 127

def put(endpoint, body)
  json = JSON.generate(body)
  response = run_request(:put, endpoint, body: json)
  return response unless response.is_a?(Hash) && response[:error]

  puts "Error PUT #{endpoint}: #{response[:error]}"
end

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



41
42
43
44
45
46
47
48
# File 'lib/domain/shared_credential/api.rb', line 41

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



36
37
38
39
# File 'lib/domain/shared_credential/api.rb', line 36

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



202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/domain/site/api.rb', line 202

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_vulnerability_scan_template_name: ENV['AR_VULNERABILITY_SCAN_TEMPLATE_NAME'],
    za_vulnerability_scan_template_name: ENV['ZA_VULNERABILITY_SCAN_TEMPLATE_NAME'],
    za_deep_dive_scanners: ENV['ZA_DEEP_DIVE_SCANNERS']
  }
end

#shared_credentialsObject



8
9
10
# File 'lib/domain/shared_credential/api.rb', line 8

def shared_credentials
  @shared_credentials ||= fetch_shared_credentials.to_a
end

#site_exists?(name) ⇒ Boolean

Returns:

  • (Boolean)


36
37
38
# File 'lib/domain/site/api.rb', line 36

def site_exists?(name)
  !fetch_site_by_name(name).nil?
end

#starts_discovery_scan(site_id) ⇒ Object



503
504
505
506
# File 'lib/domain/site/api.rb', line 503

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

#toggle_utr_site_schedule(site) ⇒ Object



231
232
233
234
235
236
237
238
239
# File 'lib/domain/site/api.rb', line 231

def toggle_utr_site_schedule(site)
  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



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

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

#update_report_query(report, query) ⇒ Object



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

def update_report_query(report, query)
  payload = report.to_hash.merge({ 'query' => query })
  payload.delete('id')
  payload.delete_if { |_k, v| v.nil? }
  endpoint = "/reports/#{report.id}"
  puts payload
  put(endpoint, payload)
end

#update_scan_schedule(scan_schedule, site_id:) ⇒ Object



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

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



76
77
78
79
80
81
82
83
84
85
# File 'lib/domain/shared_credential/api.rb', line 76

def update_shared_credential_sites(credential:, site_ids: [])
  return if credential.assigned_to_all_sites?

  new_site_ids = site_ids - credential.sites
  return if new_site_ids.empty?

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

#upsert_site_shared_credentials(site_id:) ⇒ Object



486
487
488
489
490
491
492
# File 'lib/domain/site/api.rb', line 486

def upsert_site_shared_credentials(site_id:)
  sitee(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_tag(name:) ⇒ 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


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/domain/tag/api.rb', line 21

def upsert_tag(name:)
  @cached_tags ||= initialize_cached_tags

  from_cache = @cached_tags[name]
  return from_cache unless from_cache.nil?

  from_api = find_tag_by_name(name)
  unless from_api.nil?
    @cached_tags[from_api.name] = from_api
    return from_api
  end

  id = create_tag(name:)

  if id.nil?
    puts "Tag #{name} was not created"
    return nil
  end

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

#upsert_utr_report_schedule(report_id:) ⇒ Object



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

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



222
223
224
225
226
227
228
229
# File 'lib/domain/site/api.rb', line 222

def upsert_utr_site_schedule(site_id:)
  sitee(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