Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust: implements the cache and hash-lookup flags #377

Merged
merged 4 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions rust/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ impl<A: Authenticator> Builder<A> {
keep_alive_delay_ms: 10_000,
keep_alive: true,
channel_capacity: 100,
use_cache: true,
use_hash_lookup: true,
},
authenticator,
url: Url::from_str("wss://gateway.production.vaas.gdatasecurity.de").unwrap(),
Expand Down Expand Up @@ -63,6 +65,28 @@ impl<A: Authenticator> Builder<A> {
}
}

/// Enable or disable the Cache-Lookup on the server
pub fn use_cache(self, use_cache: bool) -> Self {
Self {
options: Options {
use_cache,
..self.options
},
..self
}
}

/// Enable or disable the Hash-Lookup on the server
pub fn use_hash_lookup(self, use_hash_lookup: bool) -> Self {
Self {
options: Options {
use_hash_lookup,
..self.options
},
..self
}
}

/// Set the channel capacity of the internal results channel.
/// Increase the value if a `ResultChannelError("channel lagged by X")` is received.
/// Defaults to 100.
Expand Down
29 changes: 25 additions & 4 deletions rust/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub struct Connection {
reader_thread: ThreadHandle,
keep_alive_thread: Option<ThreadHandle>,
result_channel: ResultChannelTx,
options: Options,
}

impl Connection {
Expand All @@ -57,6 +58,7 @@ impl Connection {
reader_thread: reader_loop,
keep_alive_thread: keep_alive_loop,
result_channel: tx,
options,
}
}

Expand All @@ -73,7 +75,12 @@ impl Connection {

/// Request a verdict for a file behind a URL.
pub async fn for_url(&self, url: &Url, ct: &CancellationToken) -> VResult<VaasVerdict> {
let request = VerdictRequestForUrl::new(url, self.session_id.clone());
let request = VerdictRequestForUrl::new(
url,
self.session_id.clone(),
self.options.use_cache,
self.options.use_hash_lookup,
);
let response = Self::for_url_request(
request,
self.ws_writer.clone(),
Expand Down Expand Up @@ -104,7 +111,12 @@ impl Connection {
sha256: &Sha256,
ct: &CancellationToken,
) -> VResult<VaasVerdict> {
let request = VerdictRequest::new(sha256, self.session_id.clone());
let request = VerdictRequest::new(
sha256,
self.session_id.clone(),
self.options.use_cache,
self.options.use_hash_lookup,
);
let response = Self::for_request(
request,
self.ws_writer.clone(),
Expand All @@ -127,7 +139,11 @@ impl Connection {
S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
Bytes: From<S::Ok>,
{
let request = VerdictRequestForStream::new(self.session_id.clone());
let request = VerdictRequestForStream::new(
self.session_id.clone(),
self.options.use_cache,
self.options.use_hash_lookup,
);
let guid = request.guid().to_string();

let response = Self::for_stream_request(
Expand Down Expand Up @@ -174,7 +190,12 @@ impl Connection {
/// Request a verdict for a file.
pub async fn for_file(&self, file: &Path, ct: &CancellationToken) -> VResult<VaasVerdict> {
let sha256 = Sha256::try_from(file)?;
let request = VerdictRequest::new(&sha256, self.session_id.clone());
let request = VerdictRequest::new(
&sha256,
self.session_id.clone(),
self.options.use_cache,
self.options.use_hash_lookup,
);
let guid = request.guid().to_string();

let response = Self::for_request(
Expand Down
6 changes: 5 additions & 1 deletion rust/src/message/verdict_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ pub struct VerdictRequest {
pub kind: Kind,
pub guid: String,
pub session_id: String,
pub use_shed: bool,
pub use_cache: bool,
}

impl VerdictRequest {
pub fn new(sha256: &Sha256, session_id: String) -> Self {
pub fn new(sha256: &Sha256, session_id: String, use_cache: bool, use_shed: bool) -> Self {
Self {
guid: uuid::Uuid::new_v4().to_string(),
sha256: sha256.to_string(),
kind: Kind::VerdictRequest,
session_id,
use_cache,
use_shed,
}
}

Expand Down
6 changes: 5 additions & 1 deletion rust/src/message/verdict_request_for_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ pub struct VerdictRequestForStream {
pub kind: Kind,
pub guid: String,
pub session_id: String,
pub use_shed: bool,
pub use_cache: bool,
}

impl VerdictRequestForStream {
pub fn new(session_id: String) -> Self {
pub fn new(session_id: String, use_cache: bool, use_shed: bool) -> Self {
Self {
guid: uuid::Uuid::new_v4().to_string(),
kind: Kind::VerdictRequestForStream,
session_id,
use_cache,
use_shed,
}
}

Expand Down
6 changes: 5 additions & 1 deletion rust/src/message/verdict_request_for_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ pub struct VerdictRequestForUrl {
pub kind: Kind,
pub guid: String,
pub session_id: String,
pub use_shed: bool,
pub use_cache: bool,
}

impl VerdictRequestForUrl {
pub fn new(url: &Url, session_id: String) -> Self {
pub fn new(url: &Url, session_id: String, use_cache: bool, use_shed: bool) -> Self {
Self {
guid: uuid::Uuid::new_v4().to_string(),
url: url.to_string(),
kind: Kind::VerdictRequestForUrl,
session_id,
use_cache,
use_shed,
}
}

Expand Down
2 changes: 2 additions & 0 deletions rust/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ pub(crate) struct Options {
pub keep_alive_delay_ms: u64,
pub keep_alive: bool,
pub channel_capacity: usize,
pub use_cache: bool,
pub use_hash_lookup: bool,
}
119 changes: 117 additions & 2 deletions rust/tests/real_api_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::ops::Deref;
use vaas::auth::authenticators::{ClientCredentials, Password};
use vaas::{message::Verdict, CancellationToken, Connection, Sha256, Vaas};

async fn get_vaas() -> Connection {
async fn get_vaas_with_flags(use_cache: bool, use_hash_lookup: bool) -> Connection {
let token_url: Url = dotenv::var("TOKEN_URL")
.expect("No TOKEN_URL environment variable set to be used in the integration tests")
.parse()
Expand All @@ -19,6 +19,8 @@ async fn get_vaas() -> Connection {
let vaas_url = dotenv::var("VAAS_URL")
.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)
.url(Url::parse(&vaas_url).unwrap())
.build()
.unwrap()
Expand All @@ -27,6 +29,10 @@ async fn get_vaas() -> Connection {
.unwrap()
}

async fn get_vaas() -> Connection {
get_vaas_with_flags(true, true).await
}

#[tokio::test]
async fn connect_with_username_and_password() {
let token_url: Url = dotenv::var("TOKEN_URL")
Expand Down Expand Up @@ -124,11 +130,26 @@ async fn from_http_response_stream_returns_malicious_verdict() {
assert_eq!(Verdict::Malicious, verdict.as_ref().unwrap().verdict);
}

#[tokio::test]
async fn from_http_response_stream_no_hash_lookup_no_cache_lookup_returns_malicious_verdict() {
let result = reqwest::get("http://eicar.eu/eicar.com.txt").await;
let vaas = get_vaas_with_flags(false, false).await;

let ct = CancellationToken::from_seconds(10);
let response = result.unwrap();
let content_length: usize = response.content_length().unwrap() as usize;
let byte_stream = response.bytes_stream();
let verdict = vaas.for_stream(byte_stream, content_length, &ct).await;

assert_eq!(Verdict::Malicious, verdict.as_ref().unwrap().verdict);
}

#[tokio::test]
async fn from_string_stream_returns_malicious_verdict() {
let eicar_string = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";

let stream: Vec<Result<bytes::Bytes, std::io::Error>> = vec![Ok(bytes::Bytes::from(eicar_string))];
let stream: Vec<Result<bytes::Bytes, std::io::Error>> =
vec![Ok(bytes::Bytes::from(eicar_string))];
let stream = futures_util::stream::iter(stream);

let vaas = get_vaas().await;
Expand Down Expand Up @@ -206,6 +227,40 @@ async fn from_sha256_multiple_malicious_hash() {
assert_eq!(Verdict::Malicious, verdict_3.unwrap().verdict);
}

#[tokio::test]
async fn from_sha256_multiple_malicious_hash_without_cache() {
let vaas = get_vaas_with_flags(false, true).await;
let ct = CancellationToken::from_seconds(10);
let sha256_1 =
Sha256::try_from("000005c43196142f01d615a67b7da8a53cb0172f8e9317a2ec9a0a39a1da6fe8")
.unwrap();
let sha256_2 =
Sha256::try_from("00000b68934493af2f5954593fe8127b9dda6d4b520e78265aa5875623b58c9c")
.unwrap();
let sha256_3 =
Sha256::try_from("00000f83e3120f79a21b7b395dd3dd6a9c31ce00857f78d7cf487476ca75fd1a")
.unwrap();
let verdict_1 = vaas.for_sha256(&sha256_1, &ct).await;
let verdict_2 = vaas.for_sha256(&sha256_2, &ct).await;
let verdict_3 = vaas.for_sha256(&sha256_3, &ct).await;

assert_eq!(
"000005c43196142f01d615a67b7da8a53cb0172f8e9317a2ec9a0a39a1da6fe8",
verdict_1.as_ref().unwrap().sha256.deref()
);
assert_eq!(Verdict::Malicious, verdict_1.unwrap().verdict);
assert_eq!(
"00000b68934493af2f5954593fe8127b9dda6d4b520e78265aa5875623b58c9c",
verdict_2.as_ref().unwrap().sha256.deref()
);
assert_eq!(Verdict::Malicious, verdict_2.unwrap().verdict);
assert_eq!(
"00000f83e3120f79a21b7b395dd3dd6a9c31ce00857f78d7cf487476ca75fd1a",
verdict_3.as_ref().unwrap().sha256.deref()
);
assert_eq!(Verdict::Malicious, verdict_3.unwrap().verdict);
}

#[tokio::test]
async fn from_sha256_multiple_clean_hash() {
let vaas = get_vaas().await;
Expand Down Expand Up @@ -293,6 +348,66 @@ async fn from_file_single_malicious_file() {
std::fs::remove_file(&tmp_file).unwrap();
}

#[tokio::test]
async fn from_file_single_malicious_file_without_cache() {
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("from_file_single_malicious_file_without_cache_eicar.txt");
std::fs::write(&tmp_file, eicar.as_bytes()).unwrap();

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

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

assert_eq!(Verdict::Malicious, verdict.as_ref().unwrap().verdict);
assert_eq!(
Sha256::try_from(&tmp_file).unwrap(),
verdict.unwrap().sha256
);
std::fs::remove_file(&tmp_file).unwrap();
}

#[tokio::test]
async fn from_file_single_malicious_file_without_hash_lookup() {
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("from_file_single_malicious_file_without_hash_lookup_eicar.txt");
std::fs::write(&tmp_file, eicar.as_bytes()).unwrap();

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

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

assert_eq!(Verdict::Malicious, verdict.as_ref().unwrap().verdict);
assert_eq!(
Sha256::try_from(&tmp_file).unwrap(),
verdict.unwrap().sha256
);
std::fs::remove_file(&tmp_file).unwrap();
}

#[tokio::test]
async fn from_file_single_malicious_file_without_cache_and_without_hash_lookup() {
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("from_file_single_malicious_file_without_cache_and_without_hash_lookup_eicar.txt");
std::fs::write(&tmp_file, eicar.as_bytes()).unwrap();

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

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

assert_eq!(Verdict::Malicious, verdict.as_ref().unwrap().verdict);
assert_eq!(
Sha256::try_from(&tmp_file).unwrap(),
verdict.unwrap().sha256
);
std::fs::remove_file(&tmp_file).unwrap();
}

#[tokio::test]
async fn from_file_single_clean_file() {
let clean: [u8; 8] = [0x65, 0x0a, 0x67, 0x0a, 0x65, 0x0a, 0x62, 0x0a];
Expand Down
Loading