pub mod ast;
pub mod deobf;
pub mod macros;
pub mod rules;
pub mod utils;
mod tests;
use crate::security::detect::{Detector, Rule, ShellContext};
use anyhow::Result;
use async_trait::async_trait;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use self::ast::{CurrentAst, PsAstState, extract_env_vars};
pub struct PowerShellDetector {
ctx: Arc<ShellContext>,
rules: Vec<Arc<dyn Rule>>,
}
impl PowerShellDetector {
pub fn new(mut ctx: ShellContext, max_pending_bytes: usize) -> Self {
ctx.extensions.insert(PsAstState::new(max_pending_bytes));
ctx.extensions.insert(CurrentAst::new());
let ctx = Arc::new(ctx);
Self {
ctx,
rules: rules::get_all_rules(),
}
}
}
#[async_trait]
impl Detector for PowerShellDetector {
fn context(&self) -> &Arc<ShellContext> {
&self.ctx
}
fn rules(&self) -> &[Arc<dyn Rule>] {
&self.rules
}
async fn on_detect(&self, data: &str) -> Result<()> {
let state = self
.ctx
.extensions
.get::<PsAstState>()
.ok_or_else(|| anyhow::anyhow!("PsAstState missing"))?;
let blocks = state.push_and_commit(data).await;
if blocks.is_empty() {
return Ok(());
}
let budget = AtomicUsize::new(deobf::MAX_DEOBF_TOTAL_BYTES);
let mut all_blocks = Vec::with_capacity(blocks.len());
for block in blocks {
let expanded = deobf::deobfuscate_block(block, &self.ctx, state, 0, &budget).await;
all_blocks.extend(expanded);
}
for block in &all_blocks {
let extracted_vars = extract_env_vars(&block.tree, block.source.as_bytes());
for update in extracted_vars {
self.ctx.var.set(&update.name, update.value.clone()).await;
if update.is_export {
self.ctx.env_set(&update.name, update.value).await;
}
}
}
let current = self
.ctx
.extensions
.get::<CurrentAst>()
.ok_or_else(|| anyhow::anyhow!("CurrentAst missing"))?;
*current.blocks.write().await = all_blocks;
Ok(())
}
}