#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EvasionLayer {
Encoding,
Grammar,
ContentType,
Header,
Smuggling,
H2,
}
impl EvasionLayer {
#[must_use]
pub fn prerequisites(&self) -> &'static [EvasionLayer] {
match self {
Self::Encoding => &[Self::Grammar],
Self::Grammar => &[],
Self::ContentType => &[Self::Encoding, Self::Grammar],
Self::Header => &[],
Self::Smuggling => &[Self::ContentType, Self::Header],
Self::H2 => &[Self::Header],
}
}
#[must_use]
pub fn can_follow(&self, other: EvasionLayer) -> bool {
self.prerequisites().contains(&other) || *self == other
}
}
#[must_use]
pub fn is_valid_sequence(layers: &[EvasionLayer]) -> bool {
for (i, layer) in layers.iter().enumerate() {
let prereqs = layer.prerequisites();
for prereq in prereqs {
if !layers[..i].contains(prereq) {
return false;
}
}
}
true
}
pub fn canonicalize(layers: &mut [EvasionLayer]) {
let mut changed = true;
while changed {
changed = false;
for i in 0..layers.len().saturating_sub(1) {
let a = layers[i];
let b = layers[i + 1];
if b.prerequisites().contains(&a) {
continue; }
if a.prerequisites().contains(&b) {
layers.swap(i, i + 1);
changed = true;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_grammar_then_encoding_then_content_type() {
let seq = vec![
EvasionLayer::Grammar,
EvasionLayer::Encoding,
EvasionLayer::ContentType,
EvasionLayer::Header,
EvasionLayer::Smuggling,
];
assert!(is_valid_sequence(&seq));
}
#[test]
fn invalid_encoding_before_grammar() {
let seq = vec![EvasionLayer::Encoding, EvasionLayer::Grammar];
assert!(!is_valid_sequence(&seq));
}
#[test]
fn canonicalize_fixes_order() {
let mut seq = vec![
EvasionLayer::Encoding,
EvasionLayer::Grammar,
EvasionLayer::ContentType,
];
canonicalize(&mut seq);
assert!(is_valid_sequence(&seq));
assert_eq!(seq[0], EvasionLayer::Grammar);
assert_eq!(seq[1], EvasionLayer::Encoding);
}
#[test]
fn smuggling_requires_content_type() {
let seq = vec![EvasionLayer::Header, EvasionLayer::Smuggling];
assert!(!is_valid_sequence(&seq));
}
}