ai_root_shield 0.5.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,695 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
+ require 'time'
7
+
8
+ module AiRootShield
9
+ module Integrations
10
+ # SIEM and SOC integration connector for third-party security platforms
11
+ class SiemConnector
12
+ # Supported SIEM platforms
13
+ SUPPORTED_PLATFORMS = {
14
+ splunk: 'Splunk Enterprise/Cloud',
15
+ elastic: 'Elastic Stack (ELK)',
16
+ qradar: 'IBM QRadar',
17
+ sentinel: 'Microsoft Sentinel',
18
+ sumo_logic: 'Sumo Logic',
19
+ datadog: 'Datadog Security',
20
+ chronicle: 'Google Chronicle',
21
+ arcsight: 'Micro Focus ArcSight'
22
+ }.freeze
23
+
24
+ # Event severity levels
25
+ SEVERITY_LEVELS = {
26
+ info: 1,
27
+ low: 2,
28
+ medium: 3,
29
+ high: 4,
30
+ critical: 5
31
+ }.freeze
32
+
33
+ def initialize(platform, config = {})
34
+ @platform = platform.to_sym
35
+ @config = config
36
+ @api_endpoint = config[:api_endpoint]
37
+ @api_key = config[:api_key]
38
+ @index = config[:index] || 'ai_root_shield'
39
+ @source_type = config[:source_type] || 'mobile_security'
40
+ @timeout = config[:timeout] || 30
41
+
42
+ validate_configuration
43
+ end
44
+
45
+ # Send security events to SIEM platform
46
+ def send_security_event(analysis_results, metadata = {})
47
+ event_data = format_security_event(analysis_results, metadata)
48
+
49
+ case @platform
50
+ when :splunk
51
+ send_to_splunk(event_data)
52
+ when :elastic
53
+ send_to_elastic(event_data)
54
+ when :qradar
55
+ send_to_qradar(event_data)
56
+ when :sentinel
57
+ send_to_sentinel(event_data)
58
+ when :sumo_logic
59
+ send_to_sumo_logic(event_data)
60
+ when :datadog
61
+ send_to_datadog(event_data)
62
+ when :chronicle
63
+ send_to_chronicle(event_data)
64
+ when :arcsight
65
+ send_to_arcsight(event_data)
66
+ else
67
+ raise "Unsupported SIEM platform: #{@platform}"
68
+ end
69
+ end
70
+
71
+ # Send batch of security events
72
+ def send_batch_events(analysis_results_array, metadata = {})
73
+ batch_data = analysis_results_array.map do |results|
74
+ format_security_event(results, metadata)
75
+ end
76
+
77
+ case @platform
78
+ when :splunk
79
+ send_batch_to_splunk(batch_data)
80
+ when :elastic
81
+ send_batch_to_elastic(batch_data)
82
+ else
83
+ # For platforms without native batch support, send individually
84
+ batch_data.each { |event| send_security_event(event, metadata) }
85
+ end
86
+ end
87
+
88
+ # Create security alert based on risk threshold
89
+ def create_security_alert(analysis_results, alert_config = {})
90
+ risk_score = analysis_results[:risk_score] || 0
91
+ threshold = alert_config[:threshold] || 70
92
+
93
+ return unless risk_score >= threshold
94
+
95
+ alert_data = {
96
+ alert_id: generate_alert_id,
97
+ timestamp: Time.now.utc.iso8601,
98
+ severity: determine_alert_severity(risk_score),
99
+ title: generate_alert_title(analysis_results),
100
+ description: generate_alert_description(analysis_results),
101
+ risk_score: risk_score,
102
+ risk_factors: analysis_results[:risk_factors] || [],
103
+ platform: analysis_results[:platform] || 'unknown',
104
+ device_info: extract_device_info(analysis_results),
105
+ remediation_steps: generate_remediation_steps(analysis_results),
106
+ tags: generate_alert_tags(analysis_results)
107
+ }
108
+
109
+ send_alert(alert_data)
110
+ end
111
+
112
+ # Query SIEM for historical security events
113
+ def query_security_events(query_params = {})
114
+ case @platform
115
+ when :splunk
116
+ query_splunk(query_params)
117
+ when :elastic
118
+ query_elastic(query_params)
119
+ when :qradar
120
+ query_qradar(query_params)
121
+ else
122
+ raise "Query not supported for platform: #{@platform}"
123
+ end
124
+ end
125
+
126
+ # Generate security dashboard URL
127
+ def generate_dashboard_url(dashboard_type = 'overview')
128
+ case @platform
129
+ when :splunk
130
+ "#{@api_endpoint}/app/search/ai_root_shield_#{dashboard_type}"
131
+ when :elastic
132
+ "#{@api_endpoint}/app/kibana#/dashboard/ai-root-shield-#{dashboard_type}"
133
+ when :datadog
134
+ "#{@api_endpoint}/dashboard/ai-root-shield-#{dashboard_type}"
135
+ else
136
+ @api_endpoint
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ # Validate SIEM configuration
143
+ def validate_configuration
144
+ raise "API endpoint required for #{@platform}" unless @api_endpoint
145
+ raise "API key required for #{@platform}" unless @api_key
146
+
147
+ unless SUPPORTED_PLATFORMS.key?(@platform)
148
+ raise "Unsupported SIEM platform: #{@platform}"
149
+ end
150
+ end
151
+
152
+ # Format security event for SIEM ingestion
153
+ def format_security_event(analysis_results, metadata)
154
+ {
155
+ timestamp: Time.now.utc.iso8601,
156
+ event_type: 'mobile_security_analysis',
157
+ source: 'ai_root_shield',
158
+ version: AiRootShield::VERSION,
159
+ severity: determine_event_severity(analysis_results[:risk_score]),
160
+ risk_score: analysis_results[:risk_score] || 0,
161
+ risk_level: determine_risk_level(analysis_results[:risk_score]),
162
+ platform: analysis_results[:platform] || 'unknown',
163
+ risk_factors: analysis_results[:risk_factors] || [],
164
+ device_info: extract_device_info(analysis_results),
165
+ security_analysis: format_security_analysis(analysis_results),
166
+ compliance_status: extract_compliance_status(analysis_results),
167
+ metadata: metadata,
168
+ tags: generate_event_tags(analysis_results)
169
+ }
170
+ end
171
+
172
+ # Splunk integration methods
173
+ def send_to_splunk(event_data)
174
+ uri = URI("#{@api_endpoint}/services/collector/event")
175
+
176
+ payload = {
177
+ time: Time.now.to_i,
178
+ host: Socket.gethostname,
179
+ source: 'ai_root_shield',
180
+ sourcetype: @source_type,
181
+ index: @index,
182
+ event: event_data
183
+ }
184
+
185
+ send_http_request(uri, payload, {
186
+ 'Authorization' => "Splunk #{@api_key}",
187
+ 'Content-Type' => 'application/json'
188
+ })
189
+ end
190
+
191
+ def send_batch_to_splunk(batch_data)
192
+ uri = URI("#{@api_endpoint}/services/collector/event")
193
+
194
+ batch_payload = batch_data.map do |event|
195
+ {
196
+ time: Time.now.to_i,
197
+ host: Socket.gethostname,
198
+ source: 'ai_root_shield',
199
+ sourcetype: @source_type,
200
+ index: @index,
201
+ event: event
202
+ }
203
+ end.join("\n")
204
+
205
+ send_http_request(uri, batch_payload, {
206
+ 'Authorization' => "Splunk #{@api_key}",
207
+ 'Content-Type' => 'application/json'
208
+ })
209
+ end
210
+
211
+ def query_splunk(query_params)
212
+ search_query = build_splunk_query(query_params)
213
+ uri = URI("#{@api_endpoint}/services/search/jobs")
214
+
215
+ payload = {
216
+ search: search_query,
217
+ output_mode: 'json'
218
+ }
219
+
220
+ send_http_request(uri, payload, {
221
+ 'Authorization' => "Splunk #{@api_key}",
222
+ 'Content-Type' => 'application/x-www-form-urlencoded'
223
+ })
224
+ end
225
+
226
+ # Elastic Stack integration methods
227
+ def send_to_elastic(event_data)
228
+ uri = URI("#{@api_endpoint}/#{@index}/_doc")
229
+
230
+ send_http_request(uri, event_data, {
231
+ 'Authorization' => "ApiKey #{@api_key}",
232
+ 'Content-Type' => 'application/json'
233
+ })
234
+ end
235
+
236
+ def send_batch_to_elastic(batch_data)
237
+ uri = URI("#{@api_endpoint}/_bulk")
238
+
239
+ bulk_payload = batch_data.map do |event|
240
+ index_line = { index: { _index: @index } }
241
+ "#{index_line.to_json}\n#{event.to_json}"
242
+ end.join("\n") + "\n"
243
+
244
+ send_http_request(uri, bulk_payload, {
245
+ 'Authorization' => "ApiKey #{@api_key}",
246
+ 'Content-Type' => 'application/x-ndjson'
247
+ })
248
+ end
249
+
250
+ def query_elastic(query_params)
251
+ uri = URI("#{@api_endpoint}/#{@index}/_search")
252
+
253
+ query = build_elastic_query(query_params)
254
+
255
+ send_http_request(uri, query, {
256
+ 'Authorization' => "ApiKey #{@api_key}",
257
+ 'Content-Type' => 'application/json'
258
+ })
259
+ end
260
+
261
+ # QRadar integration methods
262
+ def send_to_qradar(event_data)
263
+ uri = URI("#{@api_endpoint}/api/siem/events")
264
+
265
+ qradar_event = format_qradar_event(event_data)
266
+
267
+ send_http_request(uri, qradar_event, {
268
+ 'SEC' => @api_key,
269
+ 'Content-Type' => 'application/json'
270
+ })
271
+ end
272
+
273
+ def query_qradar(query_params)
274
+ uri = URI("#{@api_endpoint}/api/siem/events")
275
+
276
+ query = build_qradar_query(query_params)
277
+ uri.query = URI.encode_www_form(query)
278
+
279
+ send_http_request(uri, nil, {
280
+ 'SEC' => @api_key,
281
+ 'Accept' => 'application/json'
282
+ }, 'GET')
283
+ end
284
+
285
+ # Microsoft Sentinel integration methods
286
+ def send_to_sentinel(event_data)
287
+ uri = URI("#{@api_endpoint}/api/logs")
288
+
289
+ sentinel_event = format_sentinel_event(event_data)
290
+
291
+ send_http_request(uri, sentinel_event, {
292
+ 'Authorization' => "Bearer #{@api_key}",
293
+ 'Content-Type' => 'application/json',
294
+ 'Log-Type' => 'AiRootShieldSecurityEvent'
295
+ })
296
+ end
297
+
298
+ # Sumo Logic integration methods
299
+ def send_to_sumo_logic(event_data)
300
+ uri = URI(@api_endpoint) # Sumo Logic HTTP collector endpoint
301
+
302
+ send_http_request(uri, event_data, {
303
+ 'Content-Type' => 'application/json',
304
+ 'X-Sumo-Name' => 'AI Root Shield Security Events',
305
+ 'X-Sumo-Category' => 'mobile_security'
306
+ })
307
+ end
308
+
309
+ # Datadog integration methods
310
+ def send_to_datadog(event_data)
311
+ uri = URI("#{@api_endpoint}/v1/logs")
312
+
313
+ datadog_event = format_datadog_event(event_data)
314
+
315
+ send_http_request(uri, datadog_event, {
316
+ 'DD-API-KEY' => @api_key,
317
+ 'Content-Type' => 'application/json'
318
+ })
319
+ end
320
+
321
+ # Google Chronicle integration methods
322
+ def send_to_chronicle(event_data)
323
+ uri = URI("#{@api_endpoint}/v1/unstructuredlogentries:batchCreate")
324
+
325
+ chronicle_event = format_chronicle_event(event_data)
326
+
327
+ send_http_request(uri, chronicle_event, {
328
+ 'Authorization' => "Bearer #{@api_key}",
329
+ 'Content-Type' => 'application/json'
330
+ })
331
+ end
332
+
333
+ # ArcSight integration methods
334
+ def send_to_arcsight(event_data)
335
+ uri = URI("#{@api_endpoint}/logger/api/events")
336
+
337
+ arcsight_event = format_arcsight_event(event_data)
338
+
339
+ send_http_request(uri, arcsight_event, {
340
+ 'Authorization' => "Basic #{@api_key}",
341
+ 'Content-Type' => 'application/json'
342
+ })
343
+ end
344
+
345
+ # Alert methods
346
+ def send_alert(alert_data)
347
+ case @platform
348
+ when :splunk
349
+ send_splunk_alert(alert_data)
350
+ when :elastic
351
+ send_elastic_alert(alert_data)
352
+ when :datadog
353
+ send_datadog_alert(alert_data)
354
+ else
355
+ # Send as regular event with alert flag
356
+ alert_event = alert_data.merge(event_type: 'security_alert')
357
+ send_security_event(alert_event)
358
+ end
359
+ end
360
+
361
+ # HTTP request helper
362
+ def send_http_request(uri, payload, headers, method = 'POST')
363
+ http = Net::HTTP.new(uri.host, uri.port)
364
+ http.use_ssl = uri.scheme == 'https'
365
+ http.read_timeout = @timeout
366
+
367
+ request = case method.upcase
368
+ when 'GET'
369
+ Net::HTTP::Get.new(uri)
370
+ when 'POST'
371
+ Net::HTTP::Post.new(uri)
372
+ when 'PUT'
373
+ Net::HTTP::Put.new(uri)
374
+ else
375
+ raise "Unsupported HTTP method: #{method}"
376
+ end
377
+
378
+ headers.each { |key, value| request[key] = value }
379
+
380
+ if payload
381
+ request.body = payload.is_a?(String) ? payload : payload.to_json
382
+ end
383
+
384
+ response = http.request(request)
385
+
386
+ unless response.code.start_with?('2')
387
+ raise "SIEM request failed: #{response.code} - #{response.body}"
388
+ end
389
+
390
+ JSON.parse(response.body) if response.body && !response.body.empty?
391
+ rescue JSON::ParserError
392
+ response.body
393
+ end
394
+
395
+ # Utility methods
396
+ def determine_event_severity(risk_score)
397
+ case risk_score
398
+ when 0..20
399
+ SEVERITY_LEVELS[:info]
400
+ when 21..40
401
+ SEVERITY_LEVELS[:low]
402
+ when 41..70
403
+ SEVERITY_LEVELS[:medium]
404
+ when 71..85
405
+ SEVERITY_LEVELS[:high]
406
+ else
407
+ SEVERITY_LEVELS[:critical]
408
+ end
409
+ end
410
+
411
+ def determine_alert_severity(risk_score)
412
+ case risk_score
413
+ when 70..79
414
+ 'high'
415
+ when 80..89
416
+ 'critical'
417
+ else
418
+ 'critical'
419
+ end
420
+ end
421
+
422
+ def determine_risk_level(risk_score)
423
+ case risk_score
424
+ when 0..20
425
+ 'LOW'
426
+ when 21..40
427
+ 'MEDIUM'
428
+ when 41..70
429
+ 'HIGH'
430
+ else
431
+ 'CRITICAL'
432
+ end
433
+ end
434
+
435
+ def extract_device_info(analysis_results)
436
+ {
437
+ platform: analysis_results[:platform],
438
+ device_model: analysis_results.dig(:device_info, :model),
439
+ os_version: analysis_results.dig(:device_info, :os_version),
440
+ app_version: analysis_results.dig(:device_info, :app_version)
441
+ }
442
+ end
443
+
444
+ def format_security_analysis(analysis_results)
445
+ {
446
+ safetynet: analysis_results[:safetynet],
447
+ play_integrity: analysis_results[:play_integrity],
448
+ jailbreak_detection: analysis_results[:jailbreak_detection],
449
+ code_signing: analysis_results[:code_signing],
450
+ hardware_security: analysis_results[:hardware_security],
451
+ system_integrity: analysis_results[:system_integrity]
452
+ }.compact
453
+ end
454
+
455
+ def extract_compliance_status(analysis_results)
456
+ analysis_results[:compliance] || {}
457
+ end
458
+
459
+ def generate_event_tags(analysis_results)
460
+ tags = ['mobile_security', 'ai_root_shield']
461
+ tags << analysis_results[:platform] if analysis_results[:platform]
462
+ tags << 'high_risk' if (analysis_results[:risk_score] || 0) > 70
463
+ tags << 'jailbroken' if analysis_results[:risk_factors]&.any? { |f| f.include?('JAILBREAK') }
464
+ tags << 'rooted' if analysis_results[:risk_factors]&.any? { |f| f.include?('ROOT') }
465
+ tags
466
+ end
467
+
468
+ def generate_alert_id
469
+ "ARS-ALERT-#{Time.now.strftime('%Y%m%d%H%M%S')}-#{SecureRandom.hex(4).upcase}"
470
+ end
471
+
472
+ def generate_alert_title(analysis_results)
473
+ risk_score = analysis_results[:risk_score] || 0
474
+ platform = analysis_results[:platform] || 'Unknown'
475
+
476
+ "High Risk Mobile Device Detected - #{platform} (Risk Score: #{risk_score})"
477
+ end
478
+
479
+ def generate_alert_description(analysis_results)
480
+ risk_factors = analysis_results[:risk_factors] || []
481
+ platform = analysis_results[:platform] || 'unknown'
482
+
483
+ description = "AI Root Shield detected a high-risk #{platform} device with #{risk_factors.size} security issues:\n"
484
+ risk_factors.first(5).each { |factor| description += "• #{factor}\n" }
485
+ description += "Immediate security review recommended."
486
+
487
+ description
488
+ end
489
+
490
+ def generate_remediation_steps(analysis_results)
491
+ steps = []
492
+ risk_factors = analysis_results[:risk_factors] || []
493
+
494
+ risk_factors.each do |factor|
495
+ case factor
496
+ when /ROOT|JAILBREAK/
497
+ steps << "Block device access until security compliance is restored"
498
+ when /EMULATOR/
499
+ steps << "Implement additional emulator detection measures"
500
+ when /FRIDA|XPOSED/
501
+ steps << "Deploy runtime application self-protection (RASP)"
502
+ when /INTEGRITY/
503
+ steps << "Verify application signature and code integrity"
504
+ end
505
+ end
506
+
507
+ steps.uniq.first(5)
508
+ end
509
+
510
+ def generate_alert_tags(analysis_results)
511
+ tags = generate_event_tags(analysis_results)
512
+ tags << 'security_alert'
513
+ tags << 'requires_action'
514
+ tags
515
+ end
516
+
517
+ # Platform-specific formatting methods
518
+ def format_qradar_event(event_data)
519
+ {
520
+ events: [{
521
+ eventTime: Time.now.to_i * 1000,
522
+ eventCategory: 1003, # Security event category
523
+ severity: event_data[:severity],
524
+ sourceIP: '127.0.0.1',
525
+ destinationIP: '127.0.0.1',
526
+ eventName: 'Mobile Security Analysis',
527
+ eventDescription: event_data.to_json,
528
+ properties: event_data.transform_keys(&:to_s)
529
+ }]
530
+ }
531
+ end
532
+
533
+ def format_sentinel_event(event_data)
534
+ [{
535
+ TimeGenerated: event_data[:timestamp],
536
+ EventType: event_data[:event_type],
537
+ Severity: event_data[:severity],
538
+ RiskScore: event_data[:risk_score],
539
+ Platform: event_data[:platform],
540
+ RiskFactors: event_data[:risk_factors].join(','),
541
+ EventData: event_data.to_json
542
+ }]
543
+ end
544
+
545
+ def format_datadog_event(event_data)
546
+ {
547
+ ddsource: 'ai_root_shield',
548
+ ddtags: event_data[:tags].join(','),
549
+ hostname: Socket.gethostname,
550
+ message: event_data.to_json,
551
+ service: 'mobile_security',
552
+ timestamp: Time.now.to_i * 1000
553
+ }
554
+ end
555
+
556
+ def format_chronicle_event(event_data)
557
+ {
558
+ entries: [{
559
+ logText: event_data.to_json,
560
+ timestamp: {
561
+ seconds: Time.now.to_i
562
+ }
563
+ }]
564
+ }
565
+ end
566
+
567
+ def format_arcsight_event(event_data)
568
+ {
569
+ events: [{
570
+ name: 'Mobile Security Analysis',
571
+ deviceEventClassId: 'mobile_security_analysis',
572
+ severity: event_data[:severity],
573
+ message: event_data.to_json,
574
+ deviceProduct: 'AI Root Shield',
575
+ deviceVendor: 'Ahmet KAHRAMAN',
576
+ deviceVersion: AiRootShield::VERSION
577
+ }]
578
+ }
579
+ end
580
+
581
+ # Query builders
582
+ def build_splunk_query(query_params)
583
+ base_query = "search index=#{@index} source=ai_root_shield"
584
+
585
+ if query_params[:start_time]
586
+ base_query += " earliest=#{query_params[:start_time]}"
587
+ end
588
+
589
+ if query_params[:end_time]
590
+ base_query += " latest=#{query_params[:end_time]}"
591
+ end
592
+
593
+ if query_params[:platform]
594
+ base_query += " platform=#{query_params[:platform]}"
595
+ end
596
+
597
+ if query_params[:risk_threshold]
598
+ base_query += " risk_score>=#{query_params[:risk_threshold]}"
599
+ end
600
+
601
+ base_query
602
+ end
603
+
604
+ def build_elastic_query(query_params)
605
+ query = {
606
+ query: {
607
+ bool: {
608
+ must: []
609
+ }
610
+ }
611
+ }
612
+
613
+ if query_params[:start_time] || query_params[:end_time]
614
+ range_query = { range: { timestamp: {} } }
615
+ range_query[:range][:timestamp][:gte] = query_params[:start_time] if query_params[:start_time]
616
+ range_query[:range][:timestamp][:lte] = query_params[:end_time] if query_params[:end_time]
617
+ query[:query][:bool][:must] << range_query
618
+ end
619
+
620
+ if query_params[:platform]
621
+ query[:query][:bool][:must] << { term: { platform: query_params[:platform] } }
622
+ end
623
+
624
+ if query_params[:risk_threshold]
625
+ query[:query][:bool][:must] << { range: { risk_score: { gte: query_params[:risk_threshold] } } }
626
+ end
627
+
628
+ query
629
+ end
630
+
631
+ def build_qradar_query(query_params)
632
+ query = {}
633
+
634
+ if query_params[:start_time]
635
+ query[:startTime] = query_params[:start_time]
636
+ end
637
+
638
+ if query_params[:end_time]
639
+ query[:endTime] = query_params[:end_time]
640
+ end
641
+
642
+ query[:filter] = "eventName='Mobile Security Analysis'"
643
+
644
+ query
645
+ end
646
+
647
+ # Alert senders
648
+ def send_splunk_alert(alert_data)
649
+ # Send to Splunk as notable event
650
+ uri = URI("#{@api_endpoint}/services/notable_update")
651
+
652
+ payload = {
653
+ ruleNames: ['AI Root Shield High Risk Alert'],
654
+ status: 'new',
655
+ urgency: alert_data[:severity],
656
+ comment: alert_data[:description],
657
+ eventIds: [alert_data[:alert_id]]
658
+ }
659
+
660
+ send_http_request(uri, payload, {
661
+ 'Authorization' => "Splunk #{@api_key}",
662
+ 'Content-Type' => 'application/json'
663
+ })
664
+ end
665
+
666
+ def send_elastic_alert(alert_data)
667
+ # Send to Elasticsearch as alert document
668
+ uri = URI("#{@api_endpoint}/ai_root_shield_alerts/_doc/#{alert_data[:alert_id]}")
669
+
670
+ send_http_request(uri, alert_data, {
671
+ 'Authorization' => "ApiKey #{@api_key}",
672
+ 'Content-Type' => 'application/json'
673
+ })
674
+ end
675
+
676
+ def send_datadog_alert(alert_data)
677
+ # Send to Datadog as event
678
+ uri = URI("#{@api_endpoint}/v1/events")
679
+
680
+ payload = {
681
+ title: alert_data[:title],
682
+ text: alert_data[:description],
683
+ alert_type: alert_data[:severity],
684
+ source_type_name: 'ai_root_shield',
685
+ tags: alert_data[:tags]
686
+ }
687
+
688
+ send_http_request(uri, payload, {
689
+ 'DD-API-KEY' => @api_key,
690
+ 'Content-Type' => 'application/json'
691
+ })
692
+ end
693
+ end
694
+ end
695
+ end