#![cfg_attr(coverage_nightly, coverage(off))]
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use wasmparser::{Parser, Payload, Validator};
use super::InstructionMix;
use crate::wasm::security::{PatternDetector, VulnerabilityMatch};
pub struct WasmAnalyzer {
#[allow(dead_code)]
parser: Parser,
#[allow(dead_code)]
validator: Validator,
instruction_profiler: InstructionProfiler,
pattern_detector: PatternDetector,
security_auditor: SecurityAuditor,
}
impl WasmAnalyzer {
pub fn new() -> Result<Self> {
Ok(Self {
parser: Parser::new(0),
validator: Validator::new(),
instruction_profiler: InstructionProfiler::new(),
pattern_detector: PatternDetector::new(),
security_auditor: SecurityAuditor::new(),
})
}
pub fn analyze(&self, binary: &[u8]) -> Result<AnalysisResult> {
let analysis = self.analyze_streaming(binary)?;
Ok(AnalysisResult::from(analysis))
}
pub fn analyze_streaming(&self, binary: &[u8]) -> Result<Analysis> {
let mut validator = Validator::new();
let mut profiler = self.instruction_profiler.clone();
let mut patterns = self.pattern_detector.clone();
for payload in Parser::new(0).parse_all(binary) {
let payload = payload.context("Failed to parse WASM payload")?;
validator
.payload(&payload)
.context("WASM validation failed")?;
profiler.observe(&payload);
patterns.scan(&payload)?;
}
Ok(Analysis {
module_info: ModuleInfo::from_validator(validator),
instruction_mix: profiler.finalize(),
vulnerability_patterns: patterns.finalize(),
security_report: self.security_auditor.audit(binary)?,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Analysis {
pub module_info: ModuleInfo,
pub instruction_mix: InstructionMix,
pub vulnerability_patterns: Vec<VulnerabilityMatch>,
pub security_report: SecurityReport,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisResult {
pub function_count: usize,
pub instruction_count: usize,
pub binary_size: usize,
pub memory_pages: u32,
pub max_complexity: u32,
}
impl From<Analysis> for AnalysisResult {
fn from(analysis: Analysis) -> Self {
Self {
function_count: analysis.module_info.num_functions,
instruction_count: analysis.instruction_mix.total_instructions,
binary_size: analysis.module_info.code_size,
memory_pages: analysis.module_info.num_memories as u32,
max_complexity: 10, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleInfo {
pub num_functions: usize,
pub num_imports: usize,
pub num_exports: usize,
pub num_tables: usize,
pub num_memories: usize,
pub num_globals: usize,
pub has_start_function: bool,
pub code_size: usize,
}
impl ModuleInfo {
fn from_validator(_validator: Validator) -> Self {
Self {
num_functions: 0,
num_imports: 0,
num_exports: 0,
num_tables: 0,
num_memories: 1,
num_globals: 0,
has_start_function: false,
code_size: 0,
}
}
}
include!("analyzer_profiling.rs");
include!("analyzer_security.rs");
include!("analyzer_tests.rs");
include!("analyzer_tests_security.rs");