Skip to content

Commit

Permalink
Merge pull request #398 from GDATASoftwareAG/rust/detections_file_and…
Browse files Browse the repository at this point in the history
…_mime_type

Support Detections and file/mime type #390
  • Loading branch information
lennartdohmann authored Mar 14, 2024
2 parents ff36a1a + febf5ff commit 98a0454
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 10 deletions.
2 changes: 2 additions & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,7 @@ pub use crate::vaas::Vaas;
pub use builder::Builder;
pub use cancellation::CancellationToken;
pub use connection::Connection;
pub use message::Detection;
pub use message::LibMagic;
pub use sha256::Sha256;
pub use vaas_verdict::VaasVerdict;
12 changes: 12 additions & 0 deletions rust/src/message/detection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use serde::{Deserialize, Serialize};

/// Scan engine detection
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Detection {
/// Engine ID
pub engine: i32,
/// File name
pub file_name: String,
/// Virus signature name
pub virus: String,
}
10 changes: 10 additions & 0 deletions rust/src/message/lib_magic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};

/// File and mime type as classified by https://www.darwinsys.com/file/
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct LibMagic {
/// The file type
pub file_type: String,
/// The mime type
pub mime_type: String,
}
4 changes: 4 additions & 0 deletions rust/src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

mod auth_request;
mod auth_response;
mod detection;
mod error;
mod kind;
mod lib_magic;
mod message_type;
mod open_id_connect_token_response;
mod upload_url;
Expand All @@ -15,7 +17,9 @@ mod verdict_response;

pub(super) use auth_request::AuthRequest;
pub(super) use auth_response::AuthResponse;
pub use detection::Detection;
pub(super) use error::ErrorResponse;
pub use lib_magic::LibMagic;
pub(super) use message_type::MessageType;
pub(super) use open_id_connect_token_response::OpenIdConnectTokenResponse;
pub(super) use upload_url::UploadUrl;
Expand Down
44 changes: 43 additions & 1 deletion rust/src/message/verdict_response.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use super::{Detection, LibMagic};
use crate::error::Error;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;

#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct VerdictResponse {
pub sha256: String,
pub guid: String,
pub verdict: String,
pub url: Option<String>,
pub upload_token: Option<String>,
pub detections: Option<Vec<Detection>>,
pub lib_magic: Option<LibMagic>,
}

impl TryFrom<&String> for VerdictResponse {
Expand All @@ -17,3 +20,42 @@ impl TryFrom<&String> for VerdictResponse {
serde_json::from_str(value).map_err(|e| e.into())
}
}

#[cfg(test)]
mod tests {
use crate::message::{detection::Detection, lib_magic::LibMagic, VerdictResponse};

#[test]
fn deserialize() {
let json = r#"{"sha256":"275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f","file_name":null,"verdict":"Malicious","upload_token":null,"url":null,"detections":[{"engine":2,"file_name":"/tmp/scan/051f699f-b21f-4d33-9cdd-d8b2f01e6118","virus":"EICAR-Test-File"},{"engine":3,"file_name":"/tmp/scan/051f699f-b21f-4d33-9cdd-d8b2f01e6118","virus":"EICAR_TEST_FILE"}],"lib_magic":{"file_type":"EICAR virus test files","mime_type":"text/plain"},"kind":"VerdictResponse","request_id":"ed7207a5-d65a-4400-b91c-673ff39cfd8b","guid":"ed7207a5-d65a-4400-b91c-673ff39cfd8b"}"#;
let verdict_response: VerdictResponse = serde_json::from_str(json).unwrap();

assert_eq!(
VerdictResponse {
sha256: "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f"
.to_string(),
guid: "ed7207a5-d65a-4400-b91c-673ff39cfd8b".to_string(),
verdict: "Malicious".to_string(),
url: None,
upload_token: None,
detections: Some(vec![
Detection {
engine: 2,
file_name: "/tmp/scan/051f699f-b21f-4d33-9cdd-d8b2f01e6118".to_string(),
virus: "EICAR-Test-File".to_string()
},
Detection {
engine: 3,
file_name: "/tmp/scan/051f699f-b21f-4d33-9cdd-d8b2f01e6118".to_string(),
virus: "EICAR_TEST_FILE".to_string()
}
]),
lib_magic: Some(LibMagic {
file_type: "EICAR virus test files".to_string(),
mime_type: "text/plain".to_string()
})
},
verdict_response
);
}
}
8 changes: 7 additions & 1 deletion rust/src/vaas_verdict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! The `VaaSVerdict` is the result of a request for a verdict. It contains the verdict itself and the SHA256 hash of the requested file.

use crate::error::Error;
use crate::message::{Verdict, VerdictResponse};
use crate::message::{Detection, LibMagic, Verdict, VerdictResponse};
use crate::sha256::Sha256;
use std::convert::TryFrom;

Expand All @@ -15,6 +15,10 @@ pub struct VaasVerdict {
pub sha256: Sha256,
/// Verdict for the file
pub verdict: Verdict,
/// Detections
pub detections: Option<Vec<Detection>>,
/// File and mime type as classified by https://www.darwinsys.com/file/
pub lib_magic: Option<LibMagic>,
}

impl TryFrom<VerdictResponse> for VaasVerdict {
Expand All @@ -23,6 +27,8 @@ impl TryFrom<VerdictResponse> for VaasVerdict {
Ok(Self {
sha256: Sha256::try_from(verdict_response.sha256.as_str())?,
verdict: Verdict::try_from(&verdict_response)?,
detections: verdict_response.detections,
lib_magic: verdict_response.lib_magic,
})
}
}
25 changes: 17 additions & 8 deletions rust/tests/real_api_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use reqwest::Url;
use std::convert::TryFrom;
use std::ops::Deref;
use vaas::auth::authenticators::{ClientCredentials, Password};
use vaas::{message::Verdict, CancellationToken, Connection, Sha256, Vaas};
use vaas::{message::Verdict, CancellationToken, Connection, LibMagic, Sha256, Vaas};

async fn get_vaas_with_flags(use_cache: bool, use_hash_lookup: bool) -> Connection {
let token_url: Url = dotenv::var("TOKEN_URL")
Expand All @@ -20,7 +20,7 @@ async fn get_vaas_with_flags(use_cache: bool, use_hash_lookup: bool) -> Connecti
.expect("No VAAS_URL environment variable set to be used in the integration tests");
Vaas::builder(authenticator)
.use_cache(use_cache)
.use_cache(use_hash_lookup)
.use_hash_lookup(use_hash_lookup)
.url(Url::parse(&vaas_url).unwrap())
.build()
.unwrap()
Expand Down Expand Up @@ -330,20 +330,29 @@ async fn from_sha256_multiple_unknown_hash() {
}

#[tokio::test]
async fn from_file_single_malicious_file() {
async fn for_file_single_malicious_file() {
let eicar = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";
let tmp_file = std::env::temp_dir().join("eicar.txt");
std::fs::write(&tmp_file, eicar.as_bytes()).unwrap();

let vaas = get_vaas().await;
let vaas = get_vaas_with_flags(false, false).await;
let ct = CancellationToken::from_seconds(30);

let verdict = vaas.for_file(&tmp_file, &ct).await;
let verdict = vaas.for_file(&tmp_file, &ct).await.unwrap();

assert_eq!(Verdict::Malicious, verdict.as_ref().unwrap().verdict);
assert_eq!(Verdict::Malicious, verdict.verdict);
assert_eq!(Sha256::try_from(&tmp_file).unwrap(), verdict.sha256);
let detections = verdict.detections.unwrap();
assert_eq!(2, detections[0].engine);
assert_eq!("EICAR-Test-File".to_string(), detections[0].virus);
assert_eq!(3, detections[1].engine);
assert_eq!("EICAR_TEST_FILE".to_string(), detections[1].virus);
assert_eq!(
Sha256::try_from(&tmp_file).unwrap(),
verdict.unwrap().sha256
Some(LibMagic {
file_type: "EICAR virus test files".to_string(),
mime_type: "text/plain".to_string()
}),
verdict.lib_magic
);
std::fs::remove_file(&tmp_file).unwrap();
}
Expand Down

0 comments on commit 98a0454

Please sign in to comment.