use crate::analyzer::Severity;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Program {
pub project: Option<String>,
pub attacks: Vec<Attack>,
pub suites: Vec<Suite>,
pub rules: Vec<KlrRule>,
}
impl Program {
pub fn all_attacks(&self) -> Vec<Attack> {
let mut out: Vec<Attack> = self.attacks.clone();
for suite in &self.suites {
for attack in &suite.attacks {
let mut a = attack.clone();
a.suite = Some(suite.name.clone());
out.push(a);
}
}
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Suite {
pub name: String,
pub attacks: Vec<Attack>,
pub line: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Mutation {
pub field: String,
pub generators: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Value {
Str(String),
Num(i64),
Bool(bool),
Ident(String),
}
impl Value {
pub fn as_string(&self) -> String {
match self {
Value::Str(s) => s.clone(),
Value::Num(n) => n.to_string(),
Value::Bool(b) => b.to_string(),
Value::Ident(s) => s.clone(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompareOp {
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
}
impl CompareOp {
pub fn apply(&self, lhs: i64, rhs: i64) -> bool {
match self {
CompareOp::Eq => lhs == rhs,
CompareOp::Ne => lhs != rhs,
CompareOp::Lt => lhs < rhs,
CompareOp::Gt => lhs > rhs,
CompareOp::Le => lhs <= rhs,
CompareOp::Ge => lhs >= rhs,
}
}
pub fn symbol(&self) -> &'static str {
match self {
CompareOp::Eq => "==",
CompareOp::Ne => "!=",
CompareOp::Lt => "<",
CompareOp::Gt => ">",
CompareOp::Le => "<=",
CompareOp::Ge => ">=",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Action {
pub verb: String,
pub args: Vec<Value>,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Attack {
pub name: String,
pub target: Option<String>,
pub method: Option<String>,
pub send: Vec<(String, Value)>,
pub headers: Vec<(String, Value)>,
pub payload: Option<String>,
pub repeat: Option<usize>,
pub actions: Vec<Action>,
pub expectations: Vec<Expectation>,
pub checks: Vec<String>,
pub mutations: Vec<Mutation>,
pub severity: Severity,
pub message: Option<String>,
pub suite: Option<String>,
pub line: usize,
}
impl Attack {
pub fn empty(name: String, line: usize) -> Attack {
Attack {
name,
target: None,
method: None,
send: Vec::new(),
headers: Vec::new(),
payload: None,
repeat: None,
actions: Vec::new(),
expectations: Vec::new(),
checks: Vec::new(),
mutations: Vec::new(),
severity: Severity::High,
message: None,
suite: None,
line,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Expectation {
Status { op: CompareOp, value: i64 },
ResponseContains(String),
ResponseNotContains(String),
BlockedAfter(usize),
Named { name: String, expected: bool },
}
#[derive(Debug, Clone, PartialEq)]
pub struct KlrRule {
pub name: String,
pub contains: Vec<String>,
pub reaches: Option<String>,
pub without: Vec<String>,
pub severity: Severity,
pub report: Option<String>,
pub line: usize,
}