malwaredb_server/vt/
mod.rs1use crate::State;
4use std::fmt::{Display, Formatter};
5
6use std::process::ExitCode;
7use std::time::Duration;
8
9use anyhow::{Context, Result};
10use malwaredb_virustotal::errors::VirusTotalError;
11use malwaredb_virustotal::filereport::ScanResultAttributes;
12use tracing::{debug, info};
13
14pub struct VtUpdater {
16 state: State,
18
19 pub send_samples: bool,
21}
22
23impl VtUpdater {
24 pub async fn updater(&self) -> Result<ExitCode> {
30 let hashes = self
34 .state
35 .db_type
36 .files_without_vt_records(1000)
37 .await
38 .context("Failed to retrieve hashes for querying VT")?;
39
40 let vt_client = self.state.vt_client.as_ref().context("Missing VT key")?;
41
42 for hash in hashes {
43 match vt_client.get_file_report(&hash).await {
44 Ok(result) => {
45 self.state
46 .db_type
47 .store_vt_record(&result.attributes)
48 .await
49 .context("Failed to store VT data")?;
50 }
51 Err(error) => {
52 if self.send_samples
53 && self.state.directory.is_some()
54 && error == VirusTotalError::NotFoundError
55 {
56 if let Ok(bytes) = self.state.retrieve_bytes(&hash).await {
57 match vt_client.submit_bytes(bytes, hash.clone()).await {
58 Ok(_) => {
59 info!("Sample {hash} uploaded to VT successfully.");
60 }
61 Err(e) => debug!("Error uploading unknown sample to VT: {e}"),
62 }
63 }
64 } else {
65 debug!("Error getting report for {hash}: {error}");
66 }
67 }
68 }
69
70 tokio::time::sleep(Duration::from_secs(2)).await; }
72
73 Ok(ExitCode::SUCCESS)
74 }
75
76 pub async fn loader(&self, report: &ScanResultAttributes) -> Result<()> {
82 self.state
83 .db_type
84 .store_vt_record(report)
85 .await
86 .context("Failed to store VT data")
87 }
88}
89
90#[derive(Debug, Copy, Clone, Default)]
92pub struct VtKeyMissingError;
93
94impl Display for VtKeyMissingError {
95 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96 write!(f, "No VT API Key")
97 }
98}
99
100impl std::error::Error for VtKeyMissingError {}
101
102impl TryFrom<State> for VtUpdater {
104 type Error = VtKeyMissingError;
105
106 fn try_from(state: State) -> std::result::Result<Self, Self::Error> {
107 if state.vt_client.is_none() {
108 return Err(VtKeyMissingError);
109 }
110 let send_samples = state.db_config.send_samples_to_vt;
111
112 Ok(VtUpdater {
113 state,
114 send_samples,
115 })
116 }
117}