Skip to main content

get_mitre/
lib.rs

1use fenir::database::{db_mitre_path, text_or_null, MitreData, MitreDatabase, MitreDefinition};
2use fenir::facilities::{ProgressText, Uppercase, Wording};
3use fenir::mitre::MitreRecord;
4use fenir::package::errors::write_error_message;
5use fenir::{db_mitre, flow, header, section, section_level, stamp, value};
6use regex::Regex;
7use rusqlite::fallible_iterator::FallibleIterator;
8use rusqlite::fallible_streaming_iterator::FallibleStreamingIterator;
9use rusqlite::types::Value;
10use rusqlite::Error::QueryReturnedNoRows;
11use rusqlite::{params, Connection, Row};
12use std::borrow::Cow;
13use std::collections::{BTreeSet, HashSet};
14use std::error::Error;
15use std::path::Path;
16use termint::enums::{Color, Modifier};
17use termint::widgets::ToSpan;
18use umya_spreadsheet::{reader, Worksheet};
19
20pub struct MitreEnterprise;
21impl MitreDefinition for MitreEnterprise {
22    fn define() -> MitreData {
23        MitreData {
24            name: "Enterprise",
25            url: "https://attack.mitre.org/docs/attack-excel-files/v18.1/enterprise-attack",
26            files: vec!["enterprise-attack-v18.1.xlsx"],
27        }
28    }
29}
30
31pub struct MitreIcs;
32impl MitreDefinition for MitreIcs {
33    fn define() -> MitreData {
34        MitreData {
35            name: "ICS",
36            url: "https://attack.mitre.org/docs/attack-excel-files/v18.1/ics-attack",
37            files: vec!["ics-attack-v18.1.xlsx"],
38        }
39    }
40}
41
42pub struct MitreMobile;
43impl MitreDefinition for MitreMobile {
44    fn define() -> MitreData {
45        MitreData {
46            name: "Mobile",
47            url: "https://attack.mitre.org/docs/attack-excel-files/v18.1/mobile-attack",
48            files: vec!["mobile-attack-v18.1.xlsx"],
49        }
50    }
51}
52
53macro_rules! body {
54        ($value:expr, $($with:ident),*) => {
55            $($with!($value); println!(); )*
56        };
57    }
58
59macro_rules! citations {
60    ($value:expr) => {{
61        show!(flow, "Citations", $value.citations);
62    }};
63}
64
65macro_rules! contributors {
66    ($value:expr) => {{ contributors!(",", $value) }};
67
68    ($sep:expr, $value:expr) => {{
69        show!(flow, "Contributors", $sep, $value.contributors);
70    }};
71}
72
73#[macro_export]
74macro_rules! create_structure {
75        ($name:ident, $($field:ident),*) => {
76          create_structure!($name, $($field: String),*);
77        };
78
79       ($name:ident, $($field:ident: $type:ty),*) => {
80           #[derive(Clone)]
81            pub struct $name {
82                $($field: $type),*
83            }
84        };
85    }
86
87macro_rules! description {
88    ($value:expr) => {{ show!(value, "Description", $value.description) }};
89}
90
91#[macro_export]
92macro_rules! domains {
93    ($value:expr) => {
94        section_level!(3, "Domains");
95        $value.domain.split(",").for_each(|d| {
96            value!(simplify_domain(d.trim().to_string()));
97        })
98    };
99}
100
101#[macro_export]
102macro_rules! mitre_structure {
103        ($name:ident) => {
104            create_structure!($name, id, name, url, created, modified, domain, version);
105        };
106
107        ($name:ident, $($field:ident),*) => {
108            create_structure!($name, id, name, url, created, modified, domain, version, $($field),*);
109        };
110    }
111
112macro_rules! platforms {
113    ($value:expr) => {{ show!(flow, "Platforms", $value.platforms) }};
114}
115
116macro_rules! prepare_query {
117        ($name:expr) => {
118            prepare_query!($name, $name)
119        };
120
121        ($name:expr, $source_type:expr) => {
122            format!("SELECT * FROM {} WHERE id IN (SELECT source_id FROM relationship WHERE target_id = ? AND source_type = '{}')", $name, $source_type).as_str()
123        };
124    }
125
126macro_rules! sectors {
127    ($value:expr) => {{ show!(flow, "Sectors", $value.sectors) }};
128}
129
130macro_rules! show {
131        ($with:ident, $title:expr, $($value:expr),*) => {
132            {
133                section_level!(3, $title);
134                $with!($($value),*);
135            }
136        };
137    }
138
139macro_rules! summary {
140    ($title:expr, $value:expr) => {{
141        println!(
142            "{:>1} {}",
143            "",
144            section!(format!("{} - {}", $title, $value.id), Color::DarkCyan)
145        );
146
147        stamp!($value.name);
148        stamp!($value.url);
149        stamp!($value.created);
150        stamp!($value.modified);
151        stamp!($value.version)
152    }};
153}
154
155macro_rules! unknown {
156    () => {
157        String::from("Unknown")
158    };
159}
160
161mitre_structure!(Analytic, description);
162
163mitre_structure!(
164    Asset,
165    description,
166    platforms,
167    sectors,
168    related_assets,
169    related_assets_sectors,
170    related_assets_descriptions,
171    citations
172);
173
174mitre_structure!(
175    Campaign,
176    description,
177    associated_campaigns,
178    associated_campaigns_citations,
179    first_seen,
180    first_seen_citations,
181    last_seen,
182    last_seen_citations,
183    contributors,
184    citations
185);
186
187create_structure!(Citation, name, citations, url);
188
189mitre_structure!(DetectionStrategy);
190
191mitre_structure!(
192    Group,
193    description,
194    contributors,
195    associated_groups,
196    associated_groups_citations,
197    citations
198);
199
200mitre_structure!(Mitigation, description, citations);
201
202create_structure!(
203    RelationShip,
204    source_id,
205    source_type,
206    mapping_type,
207    target_id,
208    target_type,
209    description,
210    created,
211    modified
212);
213
214mitre_structure!(
215    Software,
216    description,
217    contributors,
218    platforms,
219    aliases,
220    types,
221    citations
222);
223
224mitre_structure!(Tactic, description);
225
226#[derive(Debug)]
227pub enum ExtendedDisplayType {
228    /// Shows analytics
229    Analytics,
230
231    /// Shows assets
232    Assets,
233
234    /// Shows campaigns
235    Campaigns,
236
237    /// Shows citations
238    Citations,
239
240    /// Shows groups
241    Groups,
242
243    /// Shows the detection strategy
244    Detectionstrategy,
245
246    /// Shows the mitigations
247    Mitigations,
248
249    /// Shows relationship
250    Relationship,
251
252    /// Shows pieces of software
253    Softwares,
254
255    /// Shows tactics
256    Tactics,
257}
258
259impl ExtendedDisplayType {
260    pub fn show(&self, mitre_record: &MitreRecord) {
261        println!("{}", header!(format!("MITRE - {}", mitre_record.id)));
262        match self {
263            ExtendedDisplayType::Analytics => {
264                match find_mitre_detectionstrategy(mitre_record.id.as_str()) {
265                    Ok(detectionstrategies) => detectionstrategies.iter().for_each(|ds| {
266                        let results = find_mitre_analytics(ds.id.as_str());
267                        if let Ok(analytics) = results {
268                            analytics.iter().for_each(|a| {
269                                summary!("Analytics", a);
270
271                                println!();
272
273                                body!(a, domains, description);
274                            })
275                        } else {
276                            write_error_message(results.err().unwrap().to_string().as_str(), None);
277                        }
278                    }),
279
280                    Err(error) => write_error_message(error.to_string().as_str(), None),
281                }
282            }
283
284            ExtendedDisplayType::Assets => {
285                match find_mitre_assets(mitre_record.id.as_str()) {
286                    Ok(rows) => rows.iter().for_each(|a| {
287                        summary!("Assets", a);
288
289                        println!();
290
291                        body!(a, domains, platforms, sectors, description);
292
293                        section_level!(3, "Related assets");
294                        flow!(";", a.related_assets);
295
296                        println!();
297
298                        section_level!(3, "Related Assets Sectors");
299                        flow!(";", a.related_assets_sectors);
300
301                        println!();
302
303                        section_level!(3, "Related Assets Descriptions");
304                        flow!(a.related_assets_descriptions);
305
306                        println!();
307
308                        citations!(a);
309
310                        println!();
311                    }),
312                    Err(_) => write_error_message("Assets not found", None),
313                };
314            }
315
316            ExtendedDisplayType::Campaigns => {
317                match find_mitre_campaigns(mitre_record.id.as_str()) {
318                    Ok(campaigns) => {
319                        campaigns.iter().for_each(|c| {
320                            summary!("Campaigns", c);
321
322                            println!();
323
324                            body!(c, domains, description);
325
326                            show!(flow, "Associated campaigns", c.associated_campaigns);
327                            show_list(c.associated_campaigns_citations.as_str(), "Citations");
328
329                            println!();
330
331                            section_level!(3, "First seen");
332                            stamp!("Date", c.first_seen);
333                            show_list(c.first_seen_citations.as_str(), "Citations");
334
335                            println!();
336
337                            section_level!(3, "Last seen");
338                            stamp!("Date", c.last_seen);
339                            show_list(c.last_seen_citations.as_str(), "Citations");
340
341                            println!();
342
343                            body!(c, contributors, citations);
344                        });
345                    }
346
347                    Err(error) => write_error_message(error.to_string().as_str(), None),
348                };
349            }
350
351            ExtendedDisplayType::Citations => {
352                section_level!("Citations");
353
354                mitre_record.citations.split(",").for_each(|item| {
355                    let citation = find_mitre_citation(item);
356                    match citation {
357                        Ok(citation) => {
358                            println!("{:>4} {}:", "-", citation.name);
359
360                            stamp!(citation.url);
361                            stamp!("List", citation.citations);
362
363                            println!();
364                        }
365                        Err(err) => write_error_message(err.to_string().as_str(), None),
366                    }
367                });
368            }
369
370            ExtendedDisplayType::Detectionstrategy => {
371                match find_mitre_detectionstrategy(mitre_record.id.as_str()) {
372                    Ok(value) => {
373                        value.iter().for_each(|val| {
374                            summary!("Detection Strategies", val);
375
376                            println!();
377
378                            domains!(val);
379
380                            println!();
381
382                            section_level!(3, "Analytics");
383                            let analytics = find_mitre_analytics(val.id.as_str());
384                            if let Ok(value) = analytics {
385                                value.iter().for_each(|val| {
386                                    stamp!(val.id, val.url);
387                                })
388                            }
389                        });
390                    }
391                    Err(error) => write_error_message(error.to_string().as_str(), None),
392                }
393            }
394
395            ExtendedDisplayType::Groups => match find_mitre_groups(mitre_record.id.as_str()) {
396                Ok(results) => {
397                    results.iter().for_each(|group| {
398                        summary!("Groups", group);
399
400                        println!();
401
402                        body!(group, domains, description);
403
404                        show!(flow, "Associated Groups", group.associated_groups);
405
406                        println!();
407
408                        show!(
409                            flow,
410                            "Associated Groups Citations",
411                            group.associated_groups_citations
412                        );
413
414                        println!();
415
416                        citations!(group);
417
418                        println!();
419
420                        contributors!(";", group);
421
422                        println!();
423                    });
424                }
425                Err(err) => write_error_message(err.to_string().as_str(), None),
426            },
427
428            ExtendedDisplayType::Mitigations => {
429                match find_mitre_mitigations(mitre_record.id.as_str()) {
430                    Ok(rows) => {
431                        rows.iter().for_each(|row| {
432                            summary!("Mitigations", row);
433
434                            println!();
435
436                            body!(row, domains, description, citations);
437                        });
438                    }
439                    Err(error) => write_error_message(error.to_string().as_str(), None),
440                };
441            }
442
443            ExtendedDisplayType::Relationship => {
444                section_level!("Relationships by target");
445                match find_mitre_relationship(mitre_record.id.as_str(), true) {
446                    Ok(rows) => rows.iter().for_each(|row| {
447                        section_level!(2, row.source_type);
448
449                        stamp!(row.source_id);
450                        stamp!(row.mapping_type);
451                        stamp!(row.target_type);
452                        stamp!(row.created);
453                        stamp!(row.modified);
454
455                        println!();
456
457                        description!(row);
458
459                        println!();
460                    }),
461
462                    Err(_) => write_error_message("No relationship found", None),
463                };
464
465                section_level!("Relationships by source");
466                match find_mitre_relationship(mitre_record.id.as_str(), false) {
467                    Ok(rows) => rows.iter().for_each(|row| {
468                        section_level!(2, row.target_type);
469
470                        stamp!(row.target_id);
471                        stamp!(row.mapping_type);
472                        stamp!(row.source_type);
473                        stamp!(row.created);
474                        stamp!(row.modified);
475
476                        println!();
477
478                        description!(row);
479
480                        println!();
481                    }),
482
483                    Err(_) => write_error_message("No relationship found", None),
484                };
485            }
486
487            ExtendedDisplayType::Softwares => {
488                match find_mitre_softwares(mitre_record.id.as_str()) {
489                    Ok(software) => {
490                        software.iter().for_each(|soft| {
491                            summary!("Softwares", soft);
492                            stamp!("Type", soft.types);
493
494                            println!();
495
496                            body!(soft, domains, description);
497
498                            show!(flow, "Platforms", soft.platforms);
499
500                            println!();
501
502                            show!(flow, "Aliases", soft.aliases);
503
504                            println!();
505
506                            body!(soft, citations, contributors);
507                        });
508                    }
509                    Err(error) => write_error_message(error.to_string().as_str(), None),
510                };
511            }
512
513            ExtendedDisplayType::Tactics => mitre_record.tactics.split(",").for_each(|item| {
514                match find_mitre_tactics(item, mitre_record.domain.as_str()) {
515                    Ok(value) => {
516                        summary!("Tactics", value);
517
518                        println!();
519
520                        body!(value, domains, description);
521                    }
522                    Err(err) => write_error_message(err.to_string().as_str(), None),
523                };
524            }),
525        };
526    }
527}
528
529fn find_mitre_assets(mitre_id: &str) -> Result<Vec<Asset>, rusqlite::Error> {
530    let connection = db_mitre!()?;
531    let statement = connection.prepare(
532        "SELECT * FROM asset WHERE id IN (SELECT target_id FROM relationship WHERE source_id = ?)",
533    );
534    if let Ok(mut statement) = statement {
535        let results = statement.query(params![mitre_id]);
536        if let Ok(mut results) = results {
537            let mut assets = Vec::new();
538            while let Some(row) = results.next()? {
539                assets.push(Asset {
540                    id: row.get(0)?,
541                    name: row.get(1)?,
542                    description: row.get(2)?,
543                    url: row.get(3)?,
544                    created: row.get(4)?,
545                    modified: row.get(5)?,
546                    domain: row.get(6)?,
547                    version: row.get(7)?,
548                    platforms: row.get(8)?,
549                    sectors: row.get(9).unwrap_or(unknown!()),
550                    related_assets: row.get(10).unwrap_or(unknown!()),
551                    related_assets_sectors: row.get(11).unwrap_or(unknown!()),
552                    related_assets_descriptions: row.get(12).unwrap_or(unknown!()),
553                    citations: row.get(13).unwrap_or(unknown!()),
554                });
555            }
556
557            Ok(assets)
558        } else {
559            Err(results.err().unwrap())
560        }
561    } else {
562        Err(statement.err().unwrap())
563    }
564}
565
566fn find_mitre_mitigations(mitre_id: &str) -> Result<Vec<Mitigation>, rusqlite::Error> {
567    let connection = db_mitre!()?;
568    let statement = connection.prepare(prepare_query!("mitigation"));
569    if let Ok(mut stat) = statement {
570        let results = stat.query(params![mitre_id]);
571        if let Ok(mut results) = results {
572            let mut mitigations = vec![];
573            while let Some(row) = results.next()? {
574                mitigations.push(Mitigation {
575                    id: row.get(0)?,
576                    name: row.get(1)?,
577                    description: row.get(2)?,
578                    url: row.get(3)?,
579                    created: row.get(4)?,
580                    modified: row.get(5)?,
581                    domain: row.get(6)?,
582                    version: row.get(7)?,
583                    citations: row.get(8).unwrap_or(unknown!()),
584                });
585            }
586
587            Ok(mitigations)
588        } else {
589            Err(QueryReturnedNoRows)
590        }
591    } else {
592        let err_result = statement.unwrap_err();
593        write_error_message(err_result.to_string().as_str(), None);
594        Err(err_result)
595    }
596}
597
598fn find_mitre_campaigns(mitre_id: &str) -> Result<Vec<Campaign>, rusqlite::Error> {
599    let connection = db_mitre!()?;
600    let mut statement = connection.prepare(prepare_query!("campaign"))?;
601    let results = statement.query(params![mitre_id]);
602    if let Ok(mut rows) = results {
603        let mut campaigns = Vec::new();
604        while let Some(row) = rows.next()? {
605            campaigns.push(Campaign {
606                id: row.get(0)?,
607                name: row.get(1)?,
608                description: row.get(2)?,
609                url: row.get(3)?,
610                created: row.get(4)?,
611                modified: row.get(5)?,
612                domain: row.get(6)?,
613                version: row.get(7)?,
614                associated_campaigns: row.get(8).unwrap_or(unknown!()),
615                associated_campaigns_citations: row.get(9).unwrap_or(unknown!()),
616                first_seen: row.get(10)?,
617                first_seen_citations: row.get(11).unwrap_or(unknown!()),
618                last_seen: row.get(12)?,
619                last_seen_citations: row.get(13).unwrap_or(unknown!()),
620                contributors: row.get(14).unwrap_or(unknown!()),
621                citations: row.get(15).unwrap_or(unknown!()),
622            });
623        }
624
625        Ok(campaigns)
626    } else {
627        Err(QueryReturnedNoRows)
628    }
629}
630
631fn find_mitre_softwares(mitre_id: &str) -> Result<Vec<Software>, rusqlite::Error> {
632    let connection = db_mitre!()?;
633    let mut statement = connection.prepare(prepare_query!("software"))?;
634    let result = statement.query(params![mitre_id]);
635    if let Ok(mut rows) = result {
636        let mut softwares = Vec::new();
637
638        while let Some(soft) = rows.next()? {
639            softwares.push(Software {
640                id: soft.get(0)?,
641                name: soft.get(1)?,
642                description: soft.get(2)?,
643                url: soft.get(3)?,
644                created: soft.get(4)?,
645                modified: soft.get(5)?,
646                domain: soft.get(6)?,
647                version: soft.get(7)?,
648                contributors: soft.get(8).unwrap_or(unknown!()),
649                platforms: soft.get(9).unwrap_or(unknown!()),
650                aliases: soft.get(10).unwrap_or(unknown!()),
651                types: soft.get(11)?,
652                citations: soft.get(12).unwrap_or(unknown!()),
653            })
654        }
655        Ok(softwares)
656    } else {
657        Err(QueryReturnedNoRows)
658    }
659}
660
661fn find_mitre_groups(mitre_id: &str) -> Result<Vec<Group>, rusqlite::Error> {
662    let connection = db_mitre!()?;
663    let mut statement = connection.prepare(prepare_query!("groups", "group"))?;
664    let result = statement.query(params![mitre_id]);
665    if let Ok(mut rows) = result {
666        let mut groups = Vec::new();
667        while let Some(group) = rows.next()? {
668            groups.push(Group {
669                id: group.get(0)?,
670                name: group.get(1)?,
671                description: group.get(2)?,
672                url: group.get(3)?,
673                created: group.get(4)?,
674                modified: group.get(5)?,
675                domain: group.get(6)?,
676                version: group.get(7)?,
677                contributors: group.get(8).unwrap_or(unknown!()),
678                associated_groups: group.get(9).unwrap_or(unknown!()),
679                associated_groups_citations: group.get(10).unwrap_or(unknown!()),
680                citations: group.get(11).unwrap_or(unknown!()),
681            })
682        }
683
684        if groups.is_empty() {
685            Err(QueryReturnedNoRows)
686        } else {
687            Ok(groups)
688        }
689    } else {
690        Err(QueryReturnedNoRows)
691    }
692}
693
694fn find_mitre_detectionstrategy(mitre_id: &str) -> Result<Vec<DetectionStrategy>, rusqlite::Error> {
695    let connection = db_mitre!()?;
696    let mut statement = connection.prepare(prepare_query!("detectionstrategy"))?;
697    let results = statement.query(params![mitre_id]);
698    if let Ok(mut rows) = results {
699        let mut detection_strategy = vec![];
700        while let Some(row) = rows.next()? {
701            detection_strategy.push(DetectionStrategy {
702                id: row.get(0)?,
703                name: row.get(1)?,
704                url: row.get(2)?,
705                created: row.get(3)?,
706                modified: row.get(4)?,
707                domain: row.get(5)?,
708                version: row.get(6)?,
709            })
710        }
711
712        if detection_strategy.is_empty() {
713            Err(QueryReturnedNoRows)
714        } else {
715            Ok(detection_strategy)
716        }
717    } else {
718        Err(QueryReturnedNoRows)
719    }
720}
721
722fn find_mitre_tactics(name: &str, domains: &str) -> Result<Tactic, rusqlite::Error> {
723    let connection = db_mitre!()?;
724    connection.query_row(
725        "SELECT * FROM tactic WHERE name = ? and domain = ?",
726        params![name.trim(), domains.to_ascii_lowercase()],
727        |row| {
728            Ok(Tactic {
729                id: row.get(0).unwrap(),
730                name: row.get(1).unwrap(),
731                url: row.get(2).unwrap(),
732                description: row.get(3).unwrap(),
733                created: row.get(4).unwrap(),
734                modified: row.get(5).unwrap(),
735                domain: simplify_domain(row.get(6).unwrap()),
736                version: row.get(7).unwrap(),
737            })
738        },
739    )
740}
741
742fn find_mitre_citation(name: &str) -> Result<Citation, rusqlite::Error> {
743    let connection = db_mitre!()?;
744    connection.query_row(
745        "SELECT * FROM citation WHERE name = ?",
746        params![name],
747        |row| {
748            Ok(Citation {
749                name: row.get(0).unwrap(),
750                citations: row.get(1).unwrap(),
751                url: row.get(2).unwrap(),
752            })
753        },
754    )
755}
756
757fn find_mitre_relationship(
758    mitre_id: &str,
759    by_target_id: bool,
760) -> Result<Vec<RelationShip>, rusqlite::Error> {
761    let connection = db_mitre!()?;
762    let mut query = match by_target_id {
763        true => connection.prepare(
764            "SELECT * FROM relationship WHERE target_id = ? ORDER BY source_type, source_id",
765        )?,
766        false => connection.prepare(
767            "SELECT * FROM relationship WHERE source_id = ? ORDER BY target_type, target_id",
768        )?,
769    };
770
771    let result = query.query(params![mitre_id]);
772    let mut relationships = Vec::new();
773
774    if let Ok(mut rows) = result {
775        while let Some(value) = rows.next()? {
776            relationships.push(RelationShip {
777                source_id: value.get(0)?,
778                source_type: value.get(1)?,
779                mapping_type: value.get(2)?,
780                target_id: value.get(3)?,
781                target_type: value.get(4)?,
782                description: value.get(5).unwrap_or(unknown!()),
783                created: value.get(6)?,
784                modified: value.get(7)?,
785            })
786        }
787    }
788
789    if relationships.is_empty() {
790        Err(QueryReturnedNoRows)
791    } else {
792        Ok(relationships)
793    }
794}
795
796fn find_mitre_analytics(detection_id: &str) -> Result<Vec<Analytic>, rusqlite::Error> {
797    let connection = db_mitre!()?;
798    let mut query = connection.prepare("SELECT * FROM analytic WHERE url LIKE :detection_id")?;
799    let results = query.query(&[(":detection_id", format!("%{}%", detection_id).as_str())])?;
800    let mut analytics = Vec::new();
801    results
802        .for_each(|row: &Row| {
803            analytics.push(Analytic {
804                id: row.get(0).unwrap(),
805                name: row.get(1).unwrap(),
806                description: row.get(2).unwrap(),
807                url: row.get(3).unwrap(),
808                created: row.get(4).unwrap(),
809                modified: row.get(5).unwrap(),
810                domain: simplify_domain(row.get(6).unwrap()),
811                version: row.get(7).unwrap(),
812            })
813        })
814        .expect("Value not found in analytic table");
815
816    if analytics.is_empty() {
817        Err(QueryReturnedNoRows)
818    } else {
819        Ok(analytics)
820    }
821}
822
823pub struct MitreTables;
824
825impl MitreDatabase for MitreTables {
826    fn table_definitions(&self) -> Vec<(&'static str, &'static str)> {
827        vec![
828            (
829                "analytic",
830                "CREATE TABLE IF NOT EXISTS analytic (
831                id TEXT PRIMARY KEY,
832                name TEXT,
833                description TEXT,
834                url TEXT,
835                created TEXT,
836                modified TEXT,
837                domain TEXT,
838                version TEXT)",
839            ),
840            (
841                "asset",
842                "CREATE TABLE IF NOT EXISTS asset (
843                id TEXT PRIMARY KEY,
844                name TEXT,
845                description TEXT,
846                url TEXT,
847                created TEXT,
848                modified TEXT,
849                domain TEXT,
850                version TEXT,
851                platforms TEXT,
852                sectors TEXT,
853                related_assets TEXT,
854                related_assets_sectors TEXT,
855                related_assets_descriptions TEXT,
856                citations TEXT)",
857            ),
858            (
859                "campaign",
860                "CREATE TABLE IF NOT EXISTS campaign (
861                id TEXT PRIMARY KEY,
862                name TEXT,
863                description TEXT,
864                url TEXT,
865                created TEXT,
866                modified TEXT,
867                domain TEXT,
868                version TEXT,
869                associated_campaigns TEXT,
870                associated_campaigns_citations TEXT,
871                first_seen TEXT,
872                first_seen_citations TEXT,
873                last_seen TEXT,
874                last_seen_citations TEXT,
875                contributors TEXT,
876                citations TEXT)",
877            ),
878            (
879                "citation",
880                "CREATE TABLE IF NOT EXISTS citation (
881                name TEXT PRIMARY KEY,
882                citations TEXT,
883                url TEXT)",
884            ),
885            (
886                "datacomponent",
887                "CREATE TABLE IF NOT EXISTS datacomponent (
888                id TEXT PRIMARY KEY,
889                name TEXT,
890                description TEXT,
891                url TEXT,
892                created TEXT,
893                modified TEXT,
894                domain TEXT,
895                version TEXT)",
896            ),
897            (
898                "detectionstrategy",
899                "CREATE TABLE IF NOT EXISTS detectionstrategy (
900                id TEXT PRIMARY KEY,
901                name TEXT,
902                url TEXT,
903                created TEXT,
904                modified TEXT,
905                domain TEXT,
906                version TEXT)",
907            ),
908            (
909                "groups",
910                "CREATE TABLE IF NOT EXISTS groups (
911                id TEXT PRIMARY KEY,
912                name TEXT,
913                description TEXT,
914                url TEXT,
915                created TEXT,
916                modified TEXT,
917                domain TEXT,
918                version TEXT,
919                contributors TEXT,
920                associated_groups TEXT,
921                associated_groups_citations TEXT,
922                citations TEXT)",
923            ),
924            (
925                "mitigation",
926                "CREATE TABLE IF NOT EXISTS mitigation (
927                id TEXT PRIMARY KEY,
928                name TEXT,
929                description TEXT,
930                url TEXT,
931                created TEXT,
932                modified TEXT,
933                domain TEXT,
934                version TEXT,
935                citations TEXT)",
936            ),
937            (
938                "relationship",
939                "CREATE TABLE IF NOT EXISTS relationship (
940                source_id TEXT,
941                source_type TEXT,
942                mapping_type TEXT,
943                target_id TEXT,
944                target_type TEXT,
945                mapping_description TEXT,
946                created TEXT,
947                modified TEXT,
948
949                PRIMARY KEY(source_id, target_id))",
950            ),
951            (
952                "software",
953                "CREATE TABLE IF NOT EXISTS software (
954                id TEXT PRIMARY KEY,
955                name TEXT,
956                description TEXT,
957                url TEXT,
958                created TEXT,
959                modified TEXT,
960                domain TEXT,
961                version TEXT,
962                contributors TEXT,
963                platforms TEXT,
964                aliases TEXT,
965                type TEXT,
966                citations TEXT)",
967            ),
968            (
969                "tactic",
970                "CREATE TABLE IF NOT EXISTS tactic (
971                id TEXT PRIMARY KEY,
972                name TEXT,
973                url TEXT,
974                description TEXT,
975                created TEXT,
976                modified TEXT,
977                domain TEXT,
978                version TEXT)",
979            ),
980            (
981                "technique",
982                "CREATE TABLE IF NOT EXISTS technique (
983                id TEXT PRIMARY KEY,
984                name TEXT,
985                description TEXT,
986                url TEXT,
987                created TEXT,
988                modified TEXT,
989                domain TEXT,
990                version TEXT,
991                tactics TEXT,
992                platforms TEXT,
993                sub_techniques TEXT,
994                contributors TEXT,
995                support_remote TEXT,
996                impact_type TEXT,
997                citations TEXT,
998                tactic_type TEXT,
999                MTC_ID TEXT)",
1000            ),
1001        ]
1002    }
1003
1004    fn index_definitions(&self) -> Vec<(&'static str, &'static str)> {
1005        vec![
1006            (
1007                "asset_citations_idx",
1008                "CREATE INDEX IF NOT EXISTS asset_citations_idx on asset(citations)",
1009            ),
1010            (
1011                "campaign_citations_idx",
1012                "CREATE INDEX IF NOT EXISTS campaing_citations_idx on campaign(citations)",
1013            ),
1014            (
1015                "citation_citations_idx",
1016                "CREATE INDEX IF NOT EXISTS citation_citations_idx on citation(citations)",
1017            ),
1018            (
1019                "groups_citations_idx",
1020                "CREATE INDEX IF NOT EXISTS groups_citations_idx on groups(citations)",
1021            ),
1022            (
1023                "mitigation_citations_idx",
1024                "CREATE INDEX IF NOT EXISTS mitigation_citations_idx on mitigation(citations)",
1025            ),
1026            (
1027                "software_citations_idx",
1028                "CREATE INDEX IF NOT EXISTS software_citations_idx on software(citations)",
1029            ),
1030            (
1031                "technique_citations_idx",
1032                "CREATE INDEX IF NOT EXISTS technique_citations_idx on technique(citations)",
1033            ),
1034        ]
1035    }
1036}
1037
1038type Importer = fn(&Worksheet, &Connection, u32) -> bool;
1039
1040pub fn inject_xlsx_into_database(xlsx_file: &Path) -> Result<(), Box<dyn Error>> {
1041    let file_name = xlsx_file.file_name().unwrap();
1042    if xlsx_file.try_exists().is_ok() && xlsx_file.is_file() {
1043        let mut reader = reader::xlsx::lazy_read(xlsx_file)?;
1044        let max_sheets = reader.read_sheet_collection().get_sheet_count();
1045        let mut sheet_counter = 0;
1046
1047        let connection = db_mitre!()?;
1048        while sheet_counter < max_sheets {
1049            if let Some(sheet) = reader.get_sheet(&sheet_counter) {
1050                match sheet.get_name() {
1051                    "analytics" => import_data(sheet, &connection, analytics_data),
1052                    "assets" => import_data(sheet, &connection, assets_data),
1053                    "campaigns" => {
1054                        if !file_name.to_str().unwrap().contains("ics") {
1055                            import_data(sheet, &connection, campaigns_data);
1056                        } else {
1057                            import_data(sheet, &connection, campaigns_data_ics);
1058                        }
1059                    }
1060                    "citations" => import_data(sheet, &connection, citations_data),
1061                    "datacomponents" => import_data(sheet, &connection, datacomponents_data),
1062                    "detectionstrategies" => {
1063                        import_data(sheet, &connection, detectionstrategies_data)
1064                    }
1065                    "groups" => {
1066                        if !file_name.to_str().unwrap().contains("mobile") {
1067                            import_data(sheet, &connection, groups_data);
1068                        } else {
1069                            import_data(sheet, &connection, groups_data_mobile);
1070                        }
1071                    }
1072                    "mitigations" => import_data(sheet, &connection, mitigations_data),
1073                    "relationships" => import_data(sheet, &connection, relationships_data),
1074                    "software" => {
1075                        if !file_name.to_str().unwrap().contains("ics") {
1076                            import_data(sheet, &connection, software_data);
1077                        } else {
1078                            import_data(sheet, &connection, software_data_ics);
1079                        }
1080                    }
1081                    "tactics" => import_data(sheet, &connection, tactics_data),
1082                    "techniques" => {
1083                        if file_name.to_str().unwrap().contains("enterprise") {
1084                            import_data(sheet, &connection, techniques_data_enterprise);
1085                        } else if file_name.to_str().unwrap().contains("ics") {
1086                            import_data(sheet, &connection, techniques_data_ics);
1087                        } else if file_name.to_str().unwrap().contains("mobile") {
1088                            import_data(sheet, &connection, techniques_data_mobile);
1089                        } else {
1090                            write_error_message("No importer for sheet", Some(sheet.get_name()));
1091                        }
1092                    }
1093                    _ => write_error_message("No importer for sheet", Some(sheet.get_name())),
1094                };
1095            }
1096
1097            sheet_counter += 1;
1098        }
1099    }
1100    Ok(())
1101}
1102
1103fn import_data(sheet: &Worksheet, connection: &Connection, importer: Importer) {
1104    let (_, rows) = sheet.get_highest_column_and_row();
1105    let sheet_name = sheet.get_name();
1106    let mut progress_bar = ProgressText::new(2usize, rows as usize, sheet_name.to_string());
1107
1108    while !progress_bar.is_done() {
1109        let _ = importer(sheet, connection, progress_bar.value() as u32);
1110        progress_bar.progress();
1111    }
1112    println!();
1113}
1114
1115fn analytics_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1116    generic_data_9_columns(sheet, connection, row, "analytic")
1117}
1118
1119fn generic_data_9_columns(
1120    sheet: &Worksheet,
1121    connection: &Connection,
1122    row: u32,
1123    table_name: &str,
1124) -> bool {
1125    connection.execute(
1126        format!("INSERT INTO {table_name} (id, name, description, url, created, modified, domain, version) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").as_str(),
1127        params![
1128            sheet.get_cell_value((1, row)).get_value(),
1129            sheet.get_cell_value((3, row)).get_value(),
1130            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1131            sheet.get_cell_value((5, row)).get_value(),
1132            sheet.get_cell_value((6, row)).get_value(),
1133            sheet.get_cell_value((7, row)).get_value(),
1134            sheet.get_cell_value((8, row)).get_value(),
1135            sheet.get_cell_value((9, row)).get_value(),
1136    ]).is_ok()
1137}
1138
1139fn assets_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1140    connection.execute(
1141        "INSERT INTO asset (id, name, description, url, created, modified, domain, version, platforms, sectors, related_assets, related_assets_sectors, related_assets_descriptions, citations)
1142        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1143        params![
1144            sheet.get_cell_value((1, row)).get_value(),
1145            sheet.get_cell_value((3, row)).get_value(),
1146            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1147            sheet.get_cell_value((5, row)).get_value(),
1148            sheet.get_cell_value((6, row)).get_value(),
1149            sheet.get_cell_value((7, row)).get_value(),
1150            sheet.get_cell_value((8, row)).get_value(),
1151            sheet.get_cell_value((9, row)).get_value(),
1152            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1153            cleaning_asset_data(sheet.get_cell_value((11, row)).get_value()),
1154            cleaning_asset_data(sheet.get_cell_value((12, row)).get_value()),
1155            cleaning_asset_data(sheet.get_cell_value((13, row)).get_value()),
1156            cleaning_asset_data(sheet.get_cell_value((14, row)).get_value()),
1157            simplify_field_content(sheet.get_cell_value((15, row)).get_value()),
1158        ],
1159    ).is_ok()
1160}
1161
1162fn cleaning_asset_data(asset_data: Cow<str>) -> Value {
1163    let raw_data = asset_data
1164        .split(";")
1165        .map(|s| s.to_string())
1166        .collect::<HashSet<String>>();
1167    let filtered = raw_data
1168        .iter()
1169        .filter(|&s| !s.trim().is_empty())
1170        .collect::<Vec<_>>();
1171    let mut converted = HashSet::new();
1172
1173    filtered.iter().for_each(|s| {
1174        converted.insert(s.trim().replace(",", "").to_string());
1175    });
1176
1177    text_or_null(converted.into_iter().collect::<Vec<String>>().join(","))
1178}
1179
1180fn campaigns_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1181    connection.execute(
1182        "INSERT INTO campaign (id, name, description, url, created, modified, domain, version, associated_campaigns, associated_campaigns_citations, first_seen, first_seen_citations, last_seen, last_seen_citations, contributors, citations)\
1183        VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1184        params![
1185            sheet.get_cell_value((1, row)).get_value(),
1186            sheet.get_cell_value((3, row)).get_value(),
1187            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1188            sheet.get_cell_value((5, row)).get_value(),
1189            sheet.get_cell_value((6, row)).get_value(),
1190            sheet.get_cell_value((7, row)).get_value(),
1191            sheet.get_cell_value((8, row)).get_value(),
1192            sheet.get_cell_value((9, row)).get_value(),
1193            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1194            simplify_field_content(sheet.get_cell_value((11, row)).get_value()),
1195            sheet.get_cell_value((12, row)).get_value(),
1196            simplify_field_content(sheet.get_cell_value((13, row)).get_value()),
1197            sheet.get_cell_value((14, row)).get_value(),
1198            simplify_field_content(sheet.get_cell_value((15, row)).get_value()),
1199            simplify_field_content(sheet.get_cell_value((16, row)).get_value()),
1200            simplify_field_content(sheet.get_cell_value((17, row)).get_value()),
1201
1202        ],
1203    ).is_ok()
1204}
1205
1206fn campaigns_data_ics(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1207    connection.execute(
1208        "INSERT INTO campaign (id, name, description, url, created, modified, domain, version, associated_campaigns, associated_campaigns_citations, first_seen, first_seen_citations, last_seen, last_seen_citations, citations)\
1209        VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1210        params![
1211            sheet.get_cell_value((1, row)).get_value(),
1212            sheet.get_cell_value((3, row)).get_value(),
1213            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1214            sheet.get_cell_value((5, row)).get_value(),
1215            sheet.get_cell_value((6, row)).get_value(),
1216            sheet.get_cell_value((7, row)).get_value(),
1217            sheet.get_cell_value((8, row)).get_value(),
1218            sheet.get_cell_value((9, row)).get_value(),
1219            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1220            simplify_field_content(sheet.get_cell_value((11, row)).get_value()),
1221            sheet.get_cell_value((12, row)).get_value(),
1222            simplify_field_content(sheet.get_cell_value((13, row)).get_value()),
1223            sheet.get_cell_value((14, row)).get_value(),
1224            simplify_field_content(sheet.get_cell_value((15, row)).get_value()),
1225            simplify_field_content(sheet.get_cell_value((16, row)).get_value()),
1226
1227        ],
1228    ).is_ok()
1229}
1230
1231fn citations_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1232    connection
1233        .execute(
1234            "INSERT INTO citation (name, citations, url) VALUES (?, ?, ?)",
1235            params![
1236                sheet.get_cell_value((1, row)).get_value(),
1237                sheet.get_cell_value((2, row)).get_value(),
1238                sheet.get_cell_value((3, row)).get_value(),
1239            ],
1240        )
1241        .is_ok()
1242}
1243
1244fn datacomponents_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1245    generic_data_9_columns(sheet, connection, row, "datacomponent")
1246}
1247
1248fn detectionstrategies_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1249    connection
1250        .execute(
1251            "INSERT INTO detectionstrategy (id, name, url, created, modified, domain, version)
1252            VALUES(?, ?, ?, ?, ?, ?, ?)",
1253            params![
1254                sheet.get_cell_value((1, row)).get_value(),
1255                sheet.get_cell_value((3, row)).get_value(),
1256                sheet.get_cell_value((4, row)).get_value(),
1257                sheet.get_cell_value((5, row)).get_value(),
1258                sheet.get_cell_value((6, row)).get_value(),
1259                sheet.get_cell_value((7, row)).get_value(),
1260                sheet.get_cell_value((8, row)).get_value(),
1261            ],
1262        )
1263        .is_ok()
1264}
1265
1266fn groups_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1267    connection.execute(
1268        "INSERT INTO groups (id, name, description, url, created, modified, domain, version, contributors, associated_groups, associated_groups_citations, citations)\
1269        VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1270        params![
1271            sheet.get_cell_value((1, row)).get_value(),
1272            sheet.get_cell_value((3, row)).get_value(),
1273            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1274            sheet.get_cell_value((5, row)).get_value(),
1275            sheet.get_cell_value((6, row)).get_value(),
1276            sheet.get_cell_value((7, row)).get_value(),
1277            sheet.get_cell_value((8, row)).get_value(),
1278            sheet.get_cell_value((9, row)).get_value(),
1279            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1280            simplify_field_content(sheet.get_cell_value((11, row)).get_value()),
1281            simplify_field_content(sheet.get_cell_value((12, row)).get_value()),
1282            simplify_field_content(sheet.get_cell_value((13, row)).get_value()),
1283        ],
1284    ).is_ok()
1285}
1286
1287fn groups_data_mobile(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1288    connection.execute(
1289        "INSERT INTO groups (id, name, description, url, created, modified, domain, version, associated_groups, associated_groups_citations, contributors, citations)
1290        VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1291        params![
1292            sheet.get_cell_value((1, row)).get_value(),
1293            sheet.get_cell_value((3, row)).get_value(),
1294            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1295            sheet.get_cell_value((5, row)).get_value(),
1296            sheet.get_cell_value((6, row)).get_value(),
1297            sheet.get_cell_value((7, row)).get_value(),
1298            sheet.get_cell_value((8, row)).get_value(),
1299            sheet.get_cell_value((9, row)).get_value(),
1300            simplify_field_content(sheet.get_cell_value((11, row)).get_value()),
1301            simplify_field_content(sheet.get_cell_value((12, row)).get_value()),
1302            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1303            simplify_field_content(sheet.get_cell_value((13, row)).get_value()),
1304        ],
1305    ).is_ok()
1306}
1307
1308fn mitigations_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1309    connection.execute(
1310        "INSERT INTO mitigation (id, name, description, url, created, modified, domain, version, citations)\
1311        VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)",
1312        params![
1313          sheet.get_cell_value((1, row)).get_value(),
1314            sheet.get_cell_value((3, row)).get_value(),
1315            sheet.get_cell_value((4, row)).get_value(),
1316            remove_citations(sheet.get_cell_value((5, row)).get_value()),
1317            sheet.get_cell_value((6, row)).get_value(),
1318            sheet.get_cell_value((7, row)).get_value(),
1319            sheet.get_cell_value((8, row)).get_value(),
1320            sheet.get_cell_value((9, row)).get_value(),
1321            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1322        ],
1323    ).is_ok()
1324}
1325
1326fn relationships_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1327    connection.execute(
1328        "INSERT INTO relationship (source_id, source_type, mapping_type, target_id, target_type, mapping_description, created, modified) \
1329        VALUES(?, ?, ?, ?, ?, ?, ?, ?)",
1330        params![
1331            sheet.get_cell_value((1, row)).get_value(),
1332            sheet.get_cell_value((4, row)).get_value(),
1333            sheet.get_cell_value((5, row)).get_value(),
1334            sheet.get_cell_value((6, row)).get_value(),
1335            sheet.get_cell_value((9, row)).get_value(),
1336            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1337            sheet.get_cell_value((12, row)).get_value(),
1338            sheet.get_cell_value((13, row)).get_value(),
1339        ],
1340    ).is_ok()
1341}
1342
1343fn software_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1344    connection.execute(
1345        "INSERT INTO software (id, name, description, url, created, modified, domain, version, contributors, platforms, aliases, type, citations)
1346        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1347        params![
1348            sheet.get_cell_value((1, row)).get_value(),
1349            sheet.get_cell_value((3, row)).get_value(),
1350            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1351            sheet.get_cell_value((5, row)).get_value(),
1352            sheet.get_cell_value((6, row)).get_value(),
1353            sheet.get_cell_value((7, row)).get_value(),
1354            sheet.get_cell_value((8, row)).get_value(),
1355            simplify_field_content(sheet.get_cell_value((9, row)).get_value()),
1356            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1357            simplify_field_content(sheet.get_cell_value((11, row)).get_value()),
1358            simplify_field_content(sheet.get_cell_value((12, row)).get_value()),
1359            simplify_field_content(sheet.get_cell_value((13, row)).get_value()),
1360            simplify_field_content(sheet.get_cell_value((14, row)).get_value()),
1361        ],
1362    ).is_ok()
1363}
1364
1365fn software_data_ics(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1366    connection.execute(
1367        "INSERT INTO software (id, name, description, url, created, modified, domain, version, platforms, aliases, type, contributors, citations)
1368        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1369        params![
1370            sheet.get_cell_value((1, row)).get_value(),
1371            sheet.get_cell_value((3, row)).get_value(),
1372            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1373            sheet.get_cell_value((5, row)).get_value(),
1374            sheet.get_cell_value((6, row)).get_value(),
1375            sheet.get_cell_value((7, row)).get_value(),
1376            sheet.get_cell_value((8, row)).get_value(),
1377            simplify_field_content(sheet.get_cell_value((9, row)).get_value()),
1378            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1379            simplify_field_content(sheet.get_cell_value((11, row)).get_value()),
1380            simplify_field_content(sheet.get_cell_value((12, row)).get_value()),
1381            simplify_field_content(sheet.get_cell_value((13, row)).get_value()),
1382            simplify_field_content(sheet.get_cell_value((14, row)).get_value()),
1383        ],
1384    ).is_ok()
1385}
1386
1387fn tactics_data(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1388    generic_data_9_columns(sheet, connection, row, "tactic")
1389}
1390
1391fn techniques_data_enterprise(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1392    connection.execute(
1393        "INSERT INTO technique (id, name, description, url, created, modified, domain, version, tactics, platforms, sub_techniques, contributors, support_remote, impact_type, citations)
1394        VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", params![
1395            sheet.get_cell_value((1, row)).get_value(),
1396            sheet.get_cell_value((3, row)).get_value(),
1397            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1398            sheet.get_cell_value((5, row)).get_value(),
1399            sheet.get_cell_value((6, row)).get_value(),
1400            sheet.get_cell_value((7, row)).get_value(),
1401            sheet.get_cell_value((8, row)).get_value(),
1402            simplify_field_content(sheet.get_cell_value((9, row)).get_value()),
1403            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1404            simplify_field_content(sheet.get_cell_value((12, row)).get_value()),
1405            simplify_field_content(sheet.get_cell_value((14, row)).get_value()),
1406            simplify_field_content(sheet.get_cell_value((15, row)).get_value()),
1407            simplify_field_content(sheet.get_cell_value((16, row)).get_value()),
1408            simplify_field_content(sheet.get_cell_value((17, row)).get_value()),
1409            simplify_field_content(sheet.get_cell_value((18, row)).get_value()),
1410        ],
1411    ).is_ok()
1412}
1413
1414fn techniques_data_ics(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1415    connection.execute(
1416        "INSERT INTO technique (id, name, description, url, created, modified, domain, version, tactics, platforms, contributors, citations)
1417        VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", params![
1418            sheet.get_cell_value((1, row)).get_value(),
1419            sheet.get_cell_value((3, row)).get_value(),
1420            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1421            sheet.get_cell_value((5, row)).get_value(),
1422            sheet.get_cell_value((6, row)).get_value(),
1423            sheet.get_cell_value((7, row)).get_value(),
1424            sheet.get_cell_value((8, row)).get_value(),
1425            sheet.get_cell_value((9, row)).get_value(),
1426            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1427            simplify_field_content(sheet.get_cell_value((12, row)).get_value()),
1428            simplify_field_content(sheet.get_cell_value((13, row)).get_value()),
1429            simplify_field_content(sheet.get_cell_value((14, row)).get_value()),
1430        ],
1431    ).is_ok()
1432}
1433
1434fn techniques_data_mobile(sheet: &Worksheet, connection: &Connection, row: u32) -> bool {
1435    connection.execute(
1436        "INSERT INTO technique (id, name, description, url, created, modified, domain, version, contributors, tactics, platforms, tactic_type, MTC_ID, citations)
1437        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1438        params![
1439            sheet.get_cell_value((1, row)).get_value(),
1440            sheet.get_cell_value((3, row)).get_value(),
1441            remove_citations(sheet.get_cell_value((4, row)).get_value()),
1442            sheet.get_cell_value((5, row)).get_value(),
1443            sheet.get_cell_value((6, row)).get_value(),
1444            sheet.get_cell_value((7, row)).get_value(),
1445            sheet.get_cell_value((8, row)).get_value(),
1446            sheet.get_cell_value((9, row)).get_value(),
1447            simplify_field_content(sheet.get_cell_value((10, row)).get_value()),
1448            simplify_field_content(sheet.get_cell_value((11, row)).get_value()),
1449            simplify_field_content(sheet.get_cell_value((13, row)).get_value()),
1450            simplify_field_content(sheet.get_cell_value((14, row)).get_value()),
1451            simplify_field_content(sheet.get_cell_value((15, row)).get_value()),
1452            simplify_field_content(sheet.get_cell_value((16, row)).get_value()),
1453        ],
1454    ).is_ok()
1455}
1456
1457fn simplify_field_content(raw_value: Cow<str>) -> Value {
1458    let mut field_value = raw_value.to_string();
1459
1460    {
1461        let remove_invalid_entry_1 = Regex::new("^,,|,,$");
1462        field_value = remove_invalid_entry_1
1463            .unwrap()
1464            .replace(&field_value, "")
1465            .to_string();
1466
1467        let remove_invalid_entry_2 = Regex::new("\\),,\\(");
1468        field_value = remove_invalid_entry_2
1469            .unwrap()
1470            .replace(&field_value, "),(")
1471            .to_string();
1472    }
1473
1474    let mut citation_part = BTreeSet::new();
1475
1476    let value = field_value.split(",(").collect::<Vec<&str>>();
1477    value.iter().for_each(|&s| {
1478        citation_part.insert(
1479            s.trim()
1480                .replace("(Citation:", "")
1481                .replace(")", "")
1482                .replace(",,", ",")
1483                .replace("Citation:", "")
1484                .replacen(",", "", citation_part.len()),
1485        );
1486    });
1487
1488    let mut intermediate: Vec<String> = Vec::new();
1489    citation_part
1490        .iter()
1491        .filter(|&s| !s.is_empty())
1492        .for_each(|s| intermediate.push(s.trim().to_string()));
1493
1494    text_or_null(intermediate.join(","))
1495}
1496
1497fn remove_citations(line: Cow<str>) -> Value {
1498    let str_value = line.to_string();
1499
1500    let citations = Regex::new("\\(Citation:.*\\)").unwrap();
1501    let phase_1 = citations.replace_all(&str_value, "").to_string();
1502
1503    let blank_and_point = Regex::new("' '+[.]").unwrap();
1504    text_or_null(blank_and_point.replace_all(&phase_1, ".").to_string())
1505}
1506
1507pub fn domains() -> Vec<String> {
1508    if let Ok(connection) = db_mitre!() {
1509        let mut statement = connection
1510            .prepare("SELECT DISTINCT domain FROM technique ORDER BY domain ASC")
1511            .unwrap();
1512        let rows = statement.query([]).unwrap();
1513        let results = rows.map(|row| row.get(0)).collect::<Vec<String>>();
1514        if let Ok(result) = results {
1515            simplify_domains(result)
1516        } else {
1517            vec![]
1518        }
1519    } else {
1520        vec![]
1521    }
1522}
1523
1524fn simplify_domains(domain_list: Vec<String>) -> Vec<String> {
1525    let mut results: Vec<String> = Vec::new();
1526
1527    domain_list.into_iter().for_each(|domain| {
1528        let value = simplify_domain(domain);
1529        results.push(value);
1530    });
1531
1532    results
1533}
1534
1535fn simplify_domain(domain: String) -> String {
1536    let value = domain.split(&[',', '-']).collect::<Vec<&str>>();
1537    value[0].to_string().first_uppercase()
1538}
1539
1540pub fn sources() -> Vec<String> {
1541    if let Ok(connection) = db_mitre!() {
1542        let mut statement = connection
1543            .prepare("SELECT DISTINCT source_type FROM relationship ORDER BY source_type ASC")
1544            .unwrap();
1545        let rows = statement.query([]).unwrap();
1546        return rows.map(|row| row.get(0)).collect::<Vec<String>>().unwrap();
1547    }
1548
1549    vec![]
1550}
1551
1552pub fn show_mitre(data: &MitreRecord) {
1553    println!("{}", header!(format!("MITRE - {}", data.id)));
1554
1555    summary!("Summary", data);
1556
1557    println!();
1558
1559    show!(flow, "Platforms", data.platforms);
1560
1561    println!();
1562
1563    body!(data, domains, description);
1564
1565    section_level!("Mitre");
1566    stamp!("Sub-techniques", data.sub_techniques);
1567    stamp!(data.impact_type);
1568    stamp!(data.mtc_id);
1569
1570    println!();
1571
1572    show_tactics(data);
1573
1574    println!();
1575
1576    citations!(data);
1577
1578    println!();
1579
1580    resume_relationship(data.id.as_str());
1581
1582    println!();
1583}
1584
1585fn show_tactics(data: &MitreRecord) {
1586    section_level!("Tactics");
1587    stamp!(data.tactic_type);
1588    show_list(&data.tactics, "List");
1589}
1590
1591fn show_list(data: &str, list_label: &str) {
1592    stamp!(list_label, "");
1593    data.split(",")
1594        .for_each(|s| println!("{:>8} {}", "-", s.trim()))
1595}
1596
1597fn resume_relationship(technique_id: &str) {
1598    let connection = db_mitre!().unwrap();
1599    let mut statement = connection
1600        .prepare("SELECT source_id, source_type FROM relationship WHERE target_id = ? order by source_type, source_id")
1601        .unwrap();
1602    if let Ok(mut results) = statement.query(params![technique_id]) {
1603        print!("{:>1} {}", "", section!("Relationship"));
1604        let mut old_value = String::new();
1605        while let Some(row) = results.next().unwrap() {
1606            let section = row.get::<usize, String>(1).unwrap();
1607
1608            if section != old_value {
1609                old_value = section;
1610                print!("\n{:>6} {}: ", "-", old_value.first_uppercase());
1611            } else {
1612                print!(", ")
1613            }
1614
1615            let reference = row.get::<usize, String>(0).unwrap();
1616            print!("{}", reference);
1617        }
1618    }
1619}
1620
1621#[cfg(test)]
1622mod tests {
1623    use crate::{domains, find_mitre_detectionstrategy, sources};
1624
1625    #[test]
1626    fn test_domains() -> Result<(), String> {
1627        let domains = domains();
1628        match domains.len() == 3 {
1629            false => Err("Bad awaiting domain number".to_string()),
1630            true => Ok(()),
1631        }
1632    }
1633
1634    #[test]
1635    fn test_domain_content() -> Result<(), String> {
1636        let domains = domains();
1637        let references = vec!["Ics", "Enterprise", "Mobile"];
1638        let mut counter = 0;
1639        domains.into_iter().for_each(|domain| {
1640            if references.contains(&domain.as_str()) {
1641                counter += 1;
1642            }
1643        });
1644
1645        match counter {
1646            3 => Ok(()),
1647            _ => Err("Bad awaiting domain content".to_string()),
1648        }
1649    }
1650
1651    #[test]
1652    fn test_sources() -> Result<(), String> {
1653        let sources = sources();
1654
1655        match sources.len() {
1656            0 => Err("Bad awaiting mapping number".to_string()),
1657            _ => Ok(()),
1658        }
1659    }
1660
1661    #[test]
1662    fn test_detectionstrategy() -> Result<(), String> {
1663        let result = find_mitre_detectionstrategy("T0857");
1664
1665        match result {
1666            Ok(_) => Ok(()),
1667            Err(e) => Err(e.to_string()),
1668        }
1669    }
1670
1671    #[test]
1672    fn test_detectionstrategy_no_value() -> Result<(), String> {
1673        let result = find_mitre_detectionstrategy("foo");
1674
1675        match result {
1676            Ok(_) => Err("No value awaiting".to_string()),
1677            Err(_e) => Ok(()),
1678        }
1679    }
1680}