@@ -302,7 +302,8 @@ def synchronize(target_app_label=None):
302
302
303
303
print ("Processing Many-to-Many tables..." )
304
304
process_m2m_tables (schema_editor , ordered_models , database )
305
-
305
+ create_normal_views (database )
306
+ create_materialized_views (database )
306
307
cleanup_stale_tables (ordered_models , database )
307
308
308
309
print ("Database synchronization complete." )
@@ -488,6 +489,115 @@ def cleanup_stale_tables(models, database):
488
489
except OperationalError as e :
489
490
print ("Error dropping stale table {}: {}" .format (table , e ))
490
491
492
+ def create_normal_views (database ):
493
+ with connections [database ].cursor () as cursor :
494
+ print ("Creating normal views..." )
495
+
496
+ cursor .execute ("""
497
+ CREATE OR REPLACE VIEW vw_ticket_vulns AS
498
+ -- Query for VS Ticket Vulns
499
+ SELECT
500
+ 'vuln_scanning_tickets' as scan_source,
501
+ t.id as id,
502
+ t.opened_timestamp as first_seen,
503
+ coalesce(t.closed_timestamp, t.updated_timestamp) as last_seen,
504
+ t.cve_string as cve,
505
+ t.vuln_name as title,
506
+ vs.cpe as product,
507
+ t.ip_string as domain,
508
+ t.ip_id as domain_id,
509
+ t.port_protocol as protocol,
510
+ t.vuln_port::text as port,
511
+ t.cvss_base_score,
512
+ t.cvss_severity::text as severity,
513
+ t.organization_id,
514
+ --t.kev, as is_kev,
515
+ --t.service,
516
+ --t.risky_service = is_risky_service,
517
+ --t.os as os --Not seeing this in the ticket
518
+ te."action" as state,
519
+ t.vuln_source as data_source,
520
+ vs.description
521
+ FROM ticket t
522
+ LEFT JOIN ticket_event te
523
+ ON te.ticket_id = t.id
524
+ LEFT JOIN vuln_scan vs
525
+ ON vs.id = te.vuln_scan_id
526
+ WHERE te.event_timestamp = (
527
+ SELECT MAX(event_timestamp)
528
+ FROM ticket_event
529
+ WHERE ticket_id = t.id
530
+ )
531
+ """ )
532
+
533
+ cursor .execute ("""
534
+ CREATE OR REPLACE VIEW vw_shodan_vulns AS
535
+ -- Query for ShodanVulns
536
+ SELECT
537
+ 'shodan_vulnerability' as scan_source,
538
+ sv.shodan_vuln_uid::text as id,
539
+ null as first_seen,
540
+ sv."timestamp" as last_seen,
541
+ sv.cve as cve,
542
+ sv.name as title,
543
+ array_to_string(sv.cpe, ', ') as product,
544
+ sv.ip_string as domain,
545
+ sv.ip_id as domain_id,
546
+ sv.protocol as protocol,
547
+ sv.port as port,
548
+ sv.cvss as cvss_base_score,
549
+ sv.severity as severity,
550
+ sv.organization_uid as organization_id,
551
+ 'Open' as state,
552
+ 'Shodan' as data_source,
553
+ null as description
554
+ FROM shodan_vulns as sv
555
+ """ )
556
+
557
+ cursor .execute ("""
558
+ CREATE OR REPLACE VIEW vw_credential_breaches AS
559
+ SELECT DISTINCT
560
+ 'credential_breach' as scan_source,
561
+ cb.credential_breaches_uid::text as id,
562
+ cb.breach_date as first_seen,
563
+ cb.modified_date as last_seen,
564
+ null as cve,
565
+ cb.breach_name as title,
566
+ null as product,
567
+ ce.sub_domain_string as domain,
568
+ ce.sub_domain_id as domain_id,
569
+ 'SMTP,IMAP,POP3' as protocol, --"Unsure on this one"
570
+ null as port,
571
+ null::float as cvss_base_score,
572
+ 'Medium' as severity,
573
+ ce.organization_id,
574
+ 'Open' as state,
575
+ ds.name as data_source,
576
+ cb.description
577
+ FROM credential_breaches cb
578
+ JOIN credential_exposures ce on cb.credential_breaches_uid = ce.credential_breach_id
579
+ JOIN data_source ds on ds.data_source_uid = cb.data_source_uid
580
+ """ )
581
+ print ("Normal views created." )
582
+
583
+ def create_materialized_views (database ):
584
+ with connections [database ].cursor () as cursor :
585
+ print ("Creating materialized views..." )
586
+
587
+ cursor .execute ("DROP MATERIALIZED VIEW IF EXISTS mat_vw_combined_vulns;" )
588
+
589
+ # Example materialized view
590
+ cursor .execute ("""
591
+ CREATE MATERIALIZED VIEW IF NOT EXISTS mat_vw_combined_vulns AS
592
+ SELECT * from vw_shodan_vulns
593
+ union
594
+ SELECT * from vw_credential_breaches
595
+ """ )
596
+
597
+ # Optional refresh
598
+ cursor .execute ("REFRESH MATERIALIZED VIEW mat_vw_combined_vulns;" )
599
+
600
+ print ("Materialized views created." )
491
601
492
602
def drop_all_tables (app_label = None ):
493
603
"""Drop all tables in the database. Used with `dangerouslyforce`."""
0 commit comments