use super::probe::{Probe, ProbeTarget};
#[derive(Debug, Clone)]
pub struct DifferentialResult {
pub blocked_sql_keywords: Vec<String>,
pub blocked_sql_operators: Vec<String>,
pub blocked_sql_comments: Vec<String>,
pub blocks_sql_quotes: bool,
pub blocked_tautologies: Vec<String>,
pub blocked_xss_tags: Vec<String>,
pub blocked_xss_events: Vec<String>,
pub blocked_xss_functions: Vec<String>,
pub blocked_cmd_separators: Vec<String>,
pub blocked_cmd_commands: Vec<String>,
pub blocked_cmd_paths: Vec<String>,
pub baseline_blocked: bool,
pub total_probes: usize,
pub total_blocked: usize,
}
impl DifferentialResult {
#[must_use]
pub fn new() -> Self {
Self {
blocked_sql_keywords: Vec::new(),
blocked_sql_operators: Vec::new(),
blocked_sql_comments: Vec::new(),
blocks_sql_quotes: false,
blocked_tautologies: Vec::new(),
blocked_xss_tags: Vec::new(),
blocked_xss_events: Vec::new(),
blocked_xss_functions: Vec::new(),
blocked_cmd_separators: Vec::new(),
blocked_cmd_commands: Vec::new(),
blocked_cmd_paths: Vec::new(),
baseline_blocked: false,
total_probes: 0,
total_blocked: 0,
}
}
pub fn record(&mut self, probe: &Probe, was_blocked: bool) {
self.total_probes += 1;
if was_blocked {
self.total_blocked += 1;
}
match &probe.tests {
ProbeTarget::Baseline => self.baseline_blocked = was_blocked,
ProbeTarget::SqlKeyword(keyword) => {
if was_blocked && !self.blocked_sql_keywords.contains(keyword) {
self.blocked_sql_keywords.push(keyword.clone());
}
}
ProbeTarget::SqlOperator(operator) => {
if was_blocked && !self.blocked_sql_operators.contains(operator) {
self.blocked_sql_operators.push(operator.clone());
}
}
ProbeTarget::SqlComment(comment) => {
if was_blocked && !self.blocked_sql_comments.contains(comment) {
self.blocked_sql_comments.push(comment.clone());
}
}
ProbeTarget::SqlQuote => self.blocks_sql_quotes = was_blocked,
ProbeTarget::SqlTautology(tautology) => {
if was_blocked && !self.blocked_tautologies.contains(tautology) {
self.blocked_tautologies.push(tautology.clone());
}
}
ProbeTarget::XssTag(tag) => {
if was_blocked && !self.blocked_xss_tags.contains(tag) {
self.blocked_xss_tags.push(tag.clone());
}
}
ProbeTarget::XssEvent(event) => {
if was_blocked && !self.blocked_xss_events.contains(event) {
self.blocked_xss_events.push(event.clone());
}
}
ProbeTarget::XssExecFunction(function) => {
if was_blocked && !self.blocked_xss_functions.contains(function) {
self.blocked_xss_functions.push(function.clone());
}
}
ProbeTarget::CmdSeparator(separator) => {
if was_blocked && !self.blocked_cmd_separators.contains(separator) {
self.blocked_cmd_separators.push(separator.clone());
}
}
ProbeTarget::CmdCommand(command) => {
if was_blocked && !self.blocked_cmd_commands.contains(command) {
self.blocked_cmd_commands.push(command.clone());
}
}
ProbeTarget::CmdPath(path) => {
if was_blocked && !self.blocked_cmd_paths.contains(path) {
self.blocked_cmd_paths.push(path.clone());
}
}
}
}
}
impl Default for DifferentialResult {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::DifferentialResult;
use crate::differential::{ProbeTarget, generate_quick_probes};
#[test]
fn record_basic_results() {
let probes = generate_quick_probes();
let mut result = DifferentialResult::new();
for probe in &probes {
let blocked = match &probe.tests {
ProbeTarget::SqlTautology(_) | ProbeTarget::SqlKeyword(_) => true,
ProbeTarget::XssTag(tag) if tag == "script" => true,
_ => false,
};
result.record(probe, blocked);
}
assert_eq!(result.total_probes, probes.len());
assert!(result.total_blocked > 0);
assert!(!result.baseline_blocked);
}
#[test]
fn record_deduplicates() {
let mut result = DifferentialResult::new();
let probe = crate::differential::Probe {
payload: "test".into(),
tests: ProbeTarget::SqlKeyword("SELECT".into()),
description: "test".into(),
expected_blocked: true,
};
result.record(&probe, true);
result.record(&probe, true);
assert_eq!(result.blocked_sql_keywords.len(), 1);
assert_eq!(result.blocked_sql_keywords[0], "SELECT");
}
#[test]
fn default_impl_equivalent_to_new() {
let first = DifferentialResult::new();
let second = DifferentialResult::default();
assert_eq!(first.total_probes, second.total_probes);
assert_eq!(first.total_blocked, second.total_blocked);
}
}