#![cfg(feature = "imperva")]
use std::sync::Arc;
use std::time::Duration;
use rmcp::ErrorData;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use zendriver::{ImpervaClearanceOutcome, ZendriverError};
use crate::errors::{McpServerError, map_error};
use crate::state::SessionState;
use crate::tools::common::current_tab;
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SolveImpervaInput {
#[serde(default = "default_timeout")]
pub timeout_ms: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub poll_interval_ms: Option<u64>,
#[serde(default)]
pub with_interception: bool,
}
fn default_timeout() -> u64 {
30_000
}
#[derive(Debug, Serialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Outcome {
TokenAcquired,
ChallengeGone,
AlreadyClear,
Timeout,
}
#[derive(Debug, Serialize, JsonSchema)]
pub struct SolveImpervaOutput {
pub outcome: Outcome,
#[serde(skip_serializing_if = "Option::is_none")]
pub reese84: Option<String>,
}
pub async fn solve_imperva(
state: Arc<Mutex<SessionState>>,
input: SolveImpervaInput,
) -> Result<SolveImpervaOutput, ErrorData> {
let s = state.lock().await;
let tab = current_tab(&s).await?;
let mut bypass = tab
.imperva()
.timeout(Duration::from_millis(input.timeout_ms));
if let Some(p) = input.poll_interval_ms {
bypass = bypass.poll_interval(Duration::from_millis(p));
}
if input.with_interception {
bypass = bypass.with_interception();
}
match bypass.wait_for_clearance().await {
Ok(ImpervaClearanceOutcome::TokenAcquired { reese84, .. }) => Ok(SolveImpervaOutput {
outcome: Outcome::TokenAcquired,
reese84: Some(reese84),
}),
Ok(ImpervaClearanceOutcome::ChallengeGone) => Ok(SolveImpervaOutput {
outcome: Outcome::ChallengeGone,
reese84: None,
}),
Ok(ImpervaClearanceOutcome::AlreadyClear) => Ok(SolveImpervaOutput {
outcome: Outcome::AlreadyClear,
reese84: None,
}),
Ok(ImpervaClearanceOutcome::TimedOut { .. }) => Ok(SolveImpervaOutput {
outcome: Outcome::Timeout,
reese84: None,
}),
Err(other) => Err(map_error(McpServerError::from(ZendriverError::from(other)))),
}
}
#[cfg(test)]
#[allow(clippy::panic, clippy::unwrap_used)]
mod tests {
use super::*;
#[tokio::test]
async fn solve_imperva_with_no_browser_errors() {
let state = Arc::new(Mutex::new(SessionState::new()));
let err = solve_imperva(
state,
SolveImpervaInput {
timeout_ms: 100,
poll_interval_ms: None,
with_interception: false,
},
)
.await
.expect_err("expected BrowserNotOpen");
assert!(err.message.contains("Browser not open"));
}
}