use crate::analysis;
use crate::config::{JsStringEncoding, ObfuscationConfig};
use crate::error::Result;
use crate::transform;
use std::path::PathBuf;
#[derive(Default)]
pub struct Obfuscator {
config: ObfuscationConfig,
}
impl Obfuscator {
pub fn builder() -> ObfuscatorBuilder {
ObfuscatorBuilder {
config: ObfuscationConfig::default(),
}
}
pub fn obfuscate(&self, html: &str) -> Result<String> {
let config = self.effective_config();
let inlined;
let html: &str = if config.inline_local_resources {
inlined = crate::inline::inline_local(html, config.base_dir.as_deref());
&inlined
} else {
html
};
let symbols = analysis::analyze(html, &config);
transform::transform(html, &symbols, &config)
}
fn effective_config(&self) -> ObfuscationConfig {
use rand::{rngs::StdRng, RngExt, SeedableRng};
if !self.config.polymorphic || self.config.seed.is_some() {
return self.config.clone();
}
let mut rng = StdRng::from_rng(&mut rand::rng());
let mut c = self.config.clone();
c.randomize_tag_case = rng.random_bool(0.7);
c.shuffle_attributes = rng.random_bool(0.85);
c.unicode_escape_selectors = rng.random_bool(0.7);
c.collapse_whitespace = rng.random_bool(0.9);
if c.inject_honeypots {
let base = c.honeypot_count.max(2);
c.honeypot_count = rng.random_range(2..=base + 6);
}
c
}
}
pub struct ObfuscatorBuilder {
config: ObfuscationConfig,
}
impl ObfuscatorBuilder {
pub fn remove_comments(mut self, v: bool) -> Self {
self.config.remove_comments = v;
self
}
pub fn collapse_whitespace(mut self, v: bool) -> Self {
self.config.collapse_whitespace = v;
self
}
pub fn encode_text_entities(mut self, v: bool) -> Self {
self.config.encode_text_entities = v;
self
}
pub fn encode_attr_entities(mut self, v: bool) -> Self {
self.config.encode_attr_entities = v;
self
}
pub fn shuffle_attributes(mut self, v: bool) -> Self {
self.config.shuffle_attributes = v;
self
}
pub fn randomize_tag_case(mut self, v: bool) -> Self {
self.config.randomize_tag_case = v;
self
}
pub fn rename_classes(mut self, v: bool) -> Self {
self.config.rename_classes = v;
self
}
pub fn rename_ids(mut self, v: bool) -> Self {
self.config.rename_ids = v;
self
}
pub fn minify_css(mut self, v: bool) -> Self {
self.config.minify_css = v;
self
}
pub fn unicode_escape_selectors(mut self, v: bool) -> Self {
self.config.unicode_escape_selectors = v;
self
}
pub fn encode_js_strings(mut self, v: bool) -> Self {
self.config.js_string_encoding = if v {
JsStringEncoding::Escapes
} else {
JsStringEncoding::None
};
self
}
pub fn js_string_encoding(mut self, e: JsStringEncoding) -> Self {
self.config.js_string_encoding = e;
self
}
pub fn minify_js(mut self, v: bool) -> Self {
self.config.minify_js = v;
self
}
pub fn inject_honeypots(mut self, v: bool) -> Self {
self.config.inject_honeypots = v;
self
}
pub fn honeypot_count(mut self, n: usize) -> Self {
self.config.honeypot_count = n;
self
}
pub fn structural_obfuscation(mut self, v: bool) -> Self {
self.config.structural_obfuscation = v;
self
}
pub fn js_ast(mut self, v: bool) -> Self {
self.config.js_ast = v;
self
}
pub fn mangle_identifiers(mut self, v: bool) -> Self {
self.config.mangle_identifiers = v;
self
}
pub fn control_flow_flattening(mut self, v: bool) -> Self {
self.config.control_flow_flattening = v;
self
}
pub fn dead_code_injection(mut self, v: bool) -> Self {
self.config.dead_code_injection = v;
self
}
pub fn dead_code_threshold(mut self, t: f32) -> Self {
self.config.dead_code_threshold = t.clamp(0.0, 1.0);
self
}
pub fn inline_local_resources(mut self, v: bool) -> Self {
self.config.inline_local_resources = v;
self
}
pub fn base_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.config.base_dir = Some(dir.into());
self
}
pub fn polymorphic(mut self, v: bool) -> Self {
self.config.polymorphic = v;
self
}
pub fn seed(mut self, seed: u64) -> Self {
self.config.seed = Some(seed);
self
}
pub fn build(self) -> Obfuscator {
Obfuscator { config: self.config }
}
}