use colored::*;
use std::process::Command;
pub const ARCHITECTURES: &[&str] = &[
"aarch64", "arm", "mips64", "mips64el", "mips", "mipsel", "ppc64", "ppc64le", "ppc", "x64",
"x86",
];
pub const COMPILERS: &[&str] = &["gcc", "clang"];
pub const WINDOWS_ARCHITECTURES: &[&str] = &["x64", "x86"];
pub const WINDOWS_COMPILERS: &[&str] = &["mingw32-gcc"];
pub const LKM_ARCHITECTURES: &[&str] = &["aarch64"];
pub const LKM_COMPILERS: &[&str] = &["clang"];
pub const LKM_CWE: &[&str] = &["cwe_252", "cwe_467", "cwe_476", "cwe_676"];
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct CweTestCase {
cwe: &'static str,
architecture: &'static str,
compiler: &'static str,
check_name: &'static str,
skipped: bool,
is_lkm: bool,
}
impl CweTestCase {
fn get_filepath(&self) -> String {
let cwd = std::env::current_dir()
.unwrap()
.into_os_string()
.into_string()
.unwrap();
if self.is_lkm {
format!(
"{}/lkm_samples/build/{}_{}_{}.ko",
cwd, self.cwe, self.architecture, self.compiler
)
} else {
format!(
"{}/artificial_samples/build/{}_{}_{}.out",
cwd, self.cwe, self.architecture, self.compiler
)
}
}
pub fn run_test(
&self,
search_string: &str,
num_expected_occurences: usize,
) -> Result<(), String> {
let filepath = self.get_filepath();
if self.skipped {
println!("{} \t {}", filepath, "[SKIPPED]".yellow());
return Ok(());
}
let output = Command::new("cwe_checker")
.arg(&filepath)
.arg("--partial")
.arg(self.check_name)
.arg("--quiet")
.output()
.unwrap();
if output.status.success() {
let num_cwes = String::from_utf8(output.stdout)
.unwrap()
.lines()
.filter(|line| line.starts_with(search_string))
.count();
if num_cwes == num_expected_occurences {
println!("{} \t {}", filepath, "[OK]".green());
Ok(())
} else {
println!("{} \t {}", filepath, "[FAILED]".red());
Err(format!(
"Expected occurrences: {num_expected_occurences}. Found: {num_cwes}"
))
}
} else {
println!("{} \t {}", filepath, "[FAILED]".red());
match output.status.code() {
Some(_code) => Err(String::from_utf8(output.stdout).unwrap()
+ &String::from_utf8(output.stderr).unwrap()),
None => Err(format!("Execution failed for file {filepath}")),
}
}
}
}
pub fn mark_architecture_skipped(test_cases: &mut [CweTestCase], arch: &str) {
mark_skipped_closure(test_cases, |test| test.architecture == arch)
}
pub fn mark_compiler_skipped(test_cases: &mut [CweTestCase], comp: &str) {
mark_skipped_closure(test_cases, |test| test.compiler == comp)
}
pub fn mark_skipped(test_cases: &mut [CweTestCase], value1: &str, value2: &str) {
mark_skipped_closure(test_cases, |test| {
(test.architecture == value1 && test.compiler == value2)
|| (test.architecture == value2 && test.compiler == value1)
})
}
pub fn mark_skipped_user(test_cases: &mut [CweTestCase], value1: &str, value2: &str) {
mark_skipped_closure(test_cases, |test| {
!test.is_lkm
&& ((test.architecture == value1 && test.compiler == value2)
|| (test.architecture == value2 && test.compiler == value1))
})
}
fn mark_skipped_closure<F>(test_cases: &mut [CweTestCase], predicate: F)
where
F: Fn(&CweTestCase) -> bool,
{
for test in test_cases.iter_mut() {
if predicate(test) {
test.skipped = true;
}
}
}
pub fn linux_test_cases(cwe: &'static str, check_name: &'static str) -> Vec<CweTestCase> {
new_test_cases(cwe, ARCHITECTURES, COMPILERS, check_name, false)
.into_iter()
.filter(|test| test.architecture != "ppc" || test.compiler != "clang")
.collect()
}
pub fn windows_test_cases(cwe: &'static str, check_name: &'static str) -> Vec<CweTestCase> {
new_test_cases(
cwe,
WINDOWS_ARCHITECTURES,
WINDOWS_COMPILERS,
check_name,
false,
)
}
pub fn lkm_test_cases(cwe: &'static str, check_name: &'static str) -> Vec<CweTestCase> {
if LKM_CWE.contains(&cwe) {
new_test_cases(cwe, LKM_ARCHITECTURES, LKM_COMPILERS, check_name, true)
} else {
Vec::new()
}
}
pub fn new_test_cases(
cwe: &'static str,
architectures: &[&'static str],
compilers: &[&'static str],
check_name: &'static str,
is_lkm: bool,
) -> Vec<CweTestCase> {
let mut vec = Vec::new();
for architecture in architectures {
for compiler in compilers {
vec.push(CweTestCase {
cwe,
architecture,
compiler,
check_name,
skipped: false,
is_lkm,
});
}
}
vec
}
pub fn all_test_cases(cwe: &'static str, check_name: &'static str) -> Vec<CweTestCase> {
let mut vec = linux_test_cases(cwe, check_name);
vec.append(&mut windows_test_cases(cwe, check_name));
vec.append(&mut lkm_test_cases(cwe, check_name));
vec
}
pub fn print_errors(error_log: Vec<(String, String)>) {
for (filepath, error) in error_log {
println!("{}", format!("+++ Error for {filepath} +++").red());
println!("{error}");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn bare_metal() {
let filepath = "bare_metal_samples/test_sample.bin";
let output = Command::new("cwe_checker")
.arg(filepath)
.arg("--partial")
.arg("Memory")
.arg("--quiet")
.arg("--bare-metal-config")
.arg("../bare_metal/stm32f407vg.json")
.output()
.unwrap();
let num_cwes = String::from_utf8(output.stdout)
.unwrap()
.lines()
.filter(|line| line.starts_with("[CWE476]"))
.count();
if num_cwes >= 1 && num_cwes <= 10 {
println!("{} \t {}", filepath, "[OK]".green());
} else {
println!("{} \t {}", filepath, "[FAILED]".red());
panic!(
"Expected occurrences: Between 1 and 10. Found: {}",
num_cwes
);
}
}
#[test]
#[ignore]
fn cwe_78() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_78", "CWE78");
mark_architecture_skipped(&mut tests, "mips64");
mark_architecture_skipped(&mut tests, "mips64el");
mark_architecture_skipped(&mut tests, "mips");
mark_architecture_skipped(&mut tests, "mipsel");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_skipped(&mut tests, "x86", "gcc");
mark_skipped(&mut tests, "x86", "clang"); mark_skipped(&mut tests, "arm", "clang"); mark_skipped(&mut tests, "aarch64", "clang"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE78]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_119() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_119", "CWE119");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE119]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_125() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_119", "CWE119");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_skipped(&mut tests, "ppc", "gcc"); mark_skipped(&mut tests, "x86", "gcc"); mark_skipped(&mut tests, "x86", "clang"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 2;
if let Err(error) = test_case.run_test("[CWE125]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_134() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_134", "CWE134");
mark_architecture_skipped(&mut tests, "ppc64"); mark_skipped(&mut tests, "ppc64le", "clang"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE134]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_190() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_190", "CWE190");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 3;
if let Err(error) = test_case.run_test("[CWE190]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_215() {
let mut error_log = Vec::new();
let mut tests = linux_test_cases("cwe_476", "CWE215");
tests.extend(lkm_test_cases("cwe_476", "CWE215"));
for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE215]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_243() {
let mut error_log = Vec::new();
let mut tests = linux_test_cases("cwe_243", "CWE243");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE243]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_252() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_252", "CWE252");
let num_expected_occurences = 9;
let num_expected_occurences_lkm = 1;
mark_architecture_skipped(&mut tests, "ppc64");
mark_architecture_skipped(&mut tests, "ppc64le");
mark_skipped(&mut tests, "mips", "gcc");
mark_skipped(&mut tests, "mips64", "clang");
mark_skipped(&mut tests, "mips64el", "clang");
mark_skipped(&mut tests, "mipsel", "gcc");
mark_skipped(&mut tests, "x86", "gcc");
mark_skipped(&mut tests, "x86", "mingw32-gcc");
for test_case in tests {
let num_expected_occurences = if test_case.is_lkm {
num_expected_occurences_lkm
} else {
num_expected_occurences
};
if let Err(error) = test_case.run_test("[CWE252]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_332() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_332", "CWE332");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE332]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_337() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_337", "CWE337");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_architecture_skipped(&mut tests, "x86"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE337]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_367() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_367", "CWE367");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_skipped(&mut tests, "x86", "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE367]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_415() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_415", "CWE416");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_skipped(&mut tests, "x86", "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 2;
if let Err(error) = test_case.run_test("[CWE415]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_416() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_416", "CWE416");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_skipped(&mut tests, "x86", "mingw32-gcc"); mark_skipped(&mut tests, "x64", "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE416]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_426() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_426", "CWE426");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE426]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_467() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_467", "CWE467");
mark_skipped_user(&mut tests, "aarch64", "clang");
mark_skipped(&mut tests, "arm", "clang");
mark_skipped(&mut tests, "mips", "clang");
mark_skipped(&mut tests, "mipsel", "clang");
mark_skipped(&mut tests, "mips64", "clang");
mark_skipped(&mut tests, "mips64el", "clang");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 2;
if let Err(error) = test_case.run_test("[CWE467]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_476() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_476", "CWE476");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE476]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_560() {
let mut error_log = Vec::new();
let mut tests = linux_test_cases("cwe_560", "CWE560");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE560]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_676() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_676", "CWE676");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
if test_case.architecture == "aarch64" && test_case.compiler == "clang" {
let num_expected_occurences = 2;
if let Err(error) = test_case.run_test("[CWE676]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
} else {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE676]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_782() {
let mut error_log = Vec::new();
let tests = new_test_cases("cwe_782", &["x64"], COMPILERS, "CWE782", false);
for test_case in tests {
let num_expected_occurences = 1;
if let Err(error) = test_case.run_test("[CWE782]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_787() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_119", "CWE119");
mark_skipped(&mut tests, "arm", "gcc"); mark_skipped(&mut tests, "mips64", "gcc"); mark_skipped(&mut tests, "mips64el", "gcc"); mark_architecture_skipped(&mut tests, "mips"); mark_architecture_skipped(&mut tests, "mipsel"); mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_skipped(&mut tests, "ppc", "gcc"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 2;
if let Err(error) = test_case.run_test("[CWE787]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
panic!();
}
}
#[test]
#[ignore]
fn cwe_789() {
let mut error_log = Vec::new();
let mut tests = all_test_cases("cwe_789", "CWE789");
mark_architecture_skipped(&mut tests, "ppc64"); mark_architecture_skipped(&mut tests, "ppc64le"); mark_compiler_skipped(&mut tests, "mingw32-gcc"); for test_case in tests {
let num_expected_occurences = 2;
if let Err(error) = test_case.run_test("[CWE789]", num_expected_occurences) {
error_log.push((test_case.get_filepath(), error));
}
}
if !error_log.is_empty() {
print_errors(error_log);
}
}
}