use crate::error::{RswError, RswResult};
use crate::{RuleConfig, RuleLoader, RuleSource, RuleStage};
use http::HeaderMap;
use rswappalyzer_engine::automation::cache::AcAutomatonCache;
use rswappalyzer_engine::compiled::CompiledBundle;
use rswappalyzer_engine::{
CoreError, RuleIndexer, RuleLibrary, RuleLibraryIndex, RuleLibraryRuntime,
};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct TechDetector {
pub runtime_lib: Arc<RuleLibraryRuntime>,
pub config: RuleConfig,
pub rule_index: Option<Arc<RuleLibraryIndex>>,
}
impl TechDetector {
pub async fn new(config: RuleConfig) -> RswResult<Self> {
let (source, stage) = (&config.origin.source, &config.origin.stage);
let (runtime_lib, rule_index) = match (source, stage) {
(RuleSource::Embedded, _) => {
#[cfg(feature = "embedded-rules")]
{
Self::build_from_embedded()?
}
#[cfg(not(feature = "embedded-rules"))]
{
return Err(RswError::FeatureError(
"embedded-rules feature required".into(),
));
}
}
(RuleSource::LocalFile(path), RuleStage::Compiled) => {
let compiled_bundle = RuleLoader::new().load_compiled_bundle(path).await?;
Self::build_from_compiled(compiled_bundle, None)?
}
(RuleSource::LocalFile(path), RuleStage::Cached) => {
let rule_lib = RuleLoader::new().load_cached_rule(&path.to_path_buf()).await?;
Self::build_from_rule_library(rule_lib, true)?
}
(
RuleSource::LocalFile(_) | RuleSource::RemoteOfficial | RuleSource::RemoteCustom(_),
RuleStage::Raw,
) => {
let rule_lib = RuleLoader::new().load(&config).await?;
Self::build_from_rule_library(rule_lib, true)?
}
(RuleSource::RemoteOfficial | RuleSource::RemoteCustom(_), RuleStage::Compiled) => {
return Err(RswError::RuleConfigError(
"Remote rules do not support Compiled stage".into(),
));
}
(RuleSource::RemoteOfficial | RuleSource::RemoteCustom(_), RuleStage::Cached) => {
return Err(RswError::RuleConfigError(
"Cached stage is only supported for local files".into(),
));
}
};
Ok(Self {
runtime_lib,
config,
rule_index,
})
}
pub fn with_rules(rule_lib: RuleLibrary, config: RuleConfig) -> RswResult<Self> {
let (runtime_lib, rule_index) = Self::build_from_rule_library(rule_lib, true)?;
Ok(Self {
runtime_lib,
config,
rule_index,
})
}
#[cfg(feature = "embedded-rules")]
pub fn with_embedded_rules(config: RuleConfig) -> RswResult<Self> {
let (runtime_lib, rule_index) = Self::build_from_embedded()?;
Ok(Self {
runtime_lib,
config,
rule_index,
})
}
pub fn with_compiled_lib(
compiled_bundle: CompiledBundle,
rule_index: Option<RuleLibraryIndex>,
config: RuleConfig,
) -> RswResult<Self> {
let (runtime_lib, rule_index) = Self::build_from_compiled(compiled_bundle, rule_index)?;
Ok(Self {
runtime_lib,
config,
rule_index,
})
}
pub fn from_compiled_bytes(bytes: &[u8], config: RuleConfig) -> RswResult<Self> {
let compiled_bundle = RuleLoader::new().load_compiled_bytes(bytes)?;
let (runtime_lib, rule_index) = Self::build_from_compiled(compiled_bundle, None)?;
Ok(Self {
runtime_lib,
config,
rule_index,
})
}
pub fn from_cached_bytes(bytes: &[u8], config: RuleConfig) -> RswResult<Self> {
let rule_lib = RuleLoader::new().load_cached_rule_bytes(bytes)?;
let (runtime_lib, rule_index) = Self::build_from_rule_library(rule_lib, true)?;
Ok(Self {
runtime_lib,
config,
rule_index,
})
}
#[inline]
pub fn detect(
&self,
headers: &HeaderMap,
urls: &[&str],
body: &[u8],
) -> RswResult<super::DetectResult> {
super::detection::core::detect(self, headers, urls, body)
}
#[cfg(debug_assertions)]
#[inline]
pub fn detect_with_log(
&self,
headers: &HeaderMap,
urls: &[&str],
body: &[u8],
) -> RswResult<super::DetectResult> {
super::detection::detect_with_log(self, headers, urls, body)
}
#[cfg(feature = "embedded-rules")]
fn build_from_embedded() -> RswResult<(Arc<RuleLibraryRuntime>, Option<Arc<RuleLibraryIndex>>)> {
let compiled_bundle = crate::rswappalyzer_rules::EMBEDDED_COMPILED_BUNDLE.clone();
let runtime_lib = Self::create_runtime(compiled_bundle)?;
Ok((runtime_lib, None))
}
fn build_from_compiled(
compiled_bundle: CompiledBundle,
rule_index: Option<RuleLibraryIndex>,
) -> RswResult<(Arc<RuleLibraryRuntime>, Option<Arc<RuleLibraryIndex>>)> {
let runtime_lib = Self::create_runtime(Arc::new(compiled_bundle))?;
let rule_index = rule_index.map(Arc::new);
Ok((runtime_lib, rule_index))
}
fn build_from_rule_library(
rule_lib: RuleLibrary,
keep_index: bool,
) -> RswResult<(Arc<RuleLibraryRuntime>, Option<Arc<RuleLibraryIndex>>)> {
let rule_index = RuleLibraryIndex::from_rule_library(&rule_lib)?;
let compiled_bundle = RuleIndexer::build_compiled_library(&rule_index, None)?;
let runtime_lib = Self::create_runtime(Arc::new(compiled_bundle))?;
let rule_index = if keep_index {
Some(Arc::new(rule_index))
} else {
None
};
Ok((runtime_lib, rule_index))
}
fn create_runtime(compiled_bundle: Arc<CompiledBundle>) -> RswResult<Arc<RuleLibraryRuntime>> {
let ac_cache = AcAutomatonCache::new(&compiled_bundle)
.map_err(|e| RswError::CoreError(CoreError::from(e)))?;
Ok(Arc::new(RuleLibraryRuntime::new(compiled_bundle, Arc::new(ac_cache))))
}
pub fn config(&self) -> &RuleConfig {
&self.config
}
pub fn rule_index(&self) -> Option<&RuleLibraryIndex> {
self.rule_index.as_deref()
}
}