use http::Method;
use crate::context::ScanContext;
use crate::types::{ProbeSpec, RiskLevel};
pub trait Strategy: Send + Sync {
fn id(&self) -> &'static str;
fn name(&self) -> &'static str;
fn risk(&self) -> RiskLevel;
fn methods(&self) -> &[Method];
fn is_applicable(&self, ctx: &ScanContext) -> bool;
fn generate(&self, ctx: &ScanContext) -> Vec<ProbeSpec>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::ScanContext;
use crate::types::{ProbePair, ProbeSpec, RiskLevel, StrategyMetadata};
use http::{HeaderMap, Method};
use parlov_core::ProbeDefinition;
struct TestStrategy;
impl Strategy for TestStrategy {
fn id(&self) -> &'static str {
"test"
}
fn name(&self) -> &'static str {
"Test Strategy"
}
fn risk(&self) -> RiskLevel {
RiskLevel::Safe
}
fn methods(&self) -> &[Method] {
&[Method::GET]
}
fn is_applicable(&self, _ctx: &ScanContext) -> bool {
true
}
fn generate(&self, ctx: &ScanContext) -> Vec<ProbeSpec> {
let def = ProbeDefinition {
url: ctx.target.clone(),
method: Method::GET,
headers: HeaderMap::new(),
body: None,
};
let meta = StrategyMetadata {
strategy_id: self.id(),
strategy_name: self.name(),
risk: self.risk(),
};
vec![ProbeSpec::Pair(ProbePair {
baseline: def.clone(),
probe: def,
metadata: meta,
})]
}
}
#[test]
fn strategy_is_object_safe() {
let _: Box<dyn Strategy> = Box::new(TestStrategy);
}
#[test]
fn test_strategy_is_applicable_returns_true() {
let ctx = ScanContext {
target: "https://example.com/{id}".to_owned(),
baseline_id: "1".to_owned(),
probe_id: "999".to_owned(),
headers: HeaderMap::new(),
max_risk: RiskLevel::Safe,
known_duplicate: None,
state_field: None,
alt_credential: None,
body_template: None,
};
assert!(TestStrategy.is_applicable(&ctx));
}
#[test]
fn test_strategy_generate_returns_pair() {
let ctx = ScanContext {
target: "https://example.com/{id}".to_owned(),
baseline_id: "1".to_owned(),
probe_id: "999".to_owned(),
headers: HeaderMap::new(),
max_risk: RiskLevel::Safe,
known_duplicate: None,
state_field: None,
alt_credential: None,
body_template: None,
};
let specs = TestStrategy.generate(&ctx);
assert_eq!(specs.len(), 1);
assert!(matches!(specs[0], ProbeSpec::Pair(_)));
}
}