diff --git a/README.md b/README.md index ca35e1f86..e2d61ff35 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,10 @@ If you installed the *cwe_checker* locally, run ```bash cwe_checker BINARY ``` +If you use nix flakes, run +```bash +nix run github:fkie-cad/cwe_checker -- BINARY +``` You can adjust the behavior of most checks via a configuration file located at `src/config.json`. If you modify it, add the command line flag `--config=src/config.json` to tell the *cwe_checker* to use the modified file. For information about other available command line flags you can pass the `--help` flag to the *cwe_checker*. diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..8fcd25fe1 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1733759999, + "narHash": "sha256-463SNPWmz46iLzJKRzO3Q2b0Aurff3U1n0nYItxq7jU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a73246e2eef4c6ed172979932bc80e1404ba2d56", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..7cc166514 --- /dev/null +++ b/flake.nix @@ -0,0 +1,86 @@ +{ + description = "Nix flake for the cwe_checker with patched Ghidra as a dependency"; + + inputs = { + # depend on nixos-unstable for the latest rust version. + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = { self, nixpkgs }: + let + pkgs = nixpkgs.legacyPackages."x86_64-linux"; + # == Building Ghidra == + ghidra-cwe-checker-plugin = pkgs.ghidra.buildGhidraScripts { + pname = "cwe_checker"; + name = "cwe_checker"; + src = ./ghidra_plugin; + }; + cwe-ghidra = pkgs.ghidra.withExtensions (p: with p; [ ghidra-cwe-checker-plugin ]); + # Path to java ghidra plugin + cwe-checker-ghidra-plugins = pkgs.runCommand + "cwe-checker-ghidra-plugins" { src = ./src/ghidra/p_code_extractor; } + '' + mkdir -p $out/p_code_extractor + cp -rf $src/* $out/p_code_extractor + ''; + # Build ghidra package with analyzeHeadless in support/ instead of bin/. + # This is where the cwe_checker expects it to be + cwe-ghidra-path-fix = pkgs.stdenv.mkDerivation { + name = "analyzeHeadless"; + pname = "analyzeHeadless"; + buildInputs = [ cwe-ghidra ]; + src = cwe-ghidra; + buildPhase = '' + mkdir -p $out + cp -rf ${cwe-ghidra} $out + # cwe checker expects + mkdir -p $out/support + cp ${cwe-ghidra}/bin/ghidra-analyzeHeadless $out/support/analyzeHeadless + ''; + }; + # == Building cwe_checker == + cwe-checker-bins = pkgs.rustPlatform.buildRustPackage { + pname = "cwe_checker"; + name = "cwe_checker"; + src = ./.; + cargoLock = { + lockFile = ./Cargo.lock; + }; + }; + # Build ghidra.json + cwe-ghidra-json = pkgs.writeTextFile { + name = "GhidraConfigFile"; + text = builtins.toJSON { ghidra_path = ''${cwe-ghidra-path-fix}''; }; + }; + # creates config dir for cwe_checker + cwe-checker-configs = pkgs.runCommand "cwe-checker-configs" { src = ./src; } + '' + mkdir -p $out + cp $src/config.json $out + cp $src/lkm_config.json $out + ln -s ${cwe-ghidra-json} $out/ghidra.json + ''; + # target bin for nix run .# + cwe-checker = pkgs.writeScriptBin "cwe-checker" '' + #!/bin/sh + CWE_CHECKER_CONFIGS_PATH=${cwe-checker-configs} \ + CWE_CHECKER_GHIDRA_PLUGINS_PATH=${cwe-checker-ghidra-plugins} \ + ${cwe-checker-bins}/bin/cwe_checker $@; + ''; + in + { + devShell.x86_64-linux = pkgs.mkShell { + buildInputs = with pkgs; [ + rustc + cargo + cwe-ghidra-path-fix + ]; + shellHook = '' + export CWE_CHECKER_CONFIGS_PATH=${cwe-checker-configs} \ + export CWE_CHECKER_GHIDRA_PLUGINS_PATH=${cwe-checker-ghidra-plugins} \ + ''; + }; + packages.x86_64-linux.default = cwe-checker; + }; +} + diff --git a/src/cwe_checker_lib/src/utils/ghidra.rs b/src/cwe_checker_lib/src/utils/ghidra.rs index da7441f16..166fd9902 100644 --- a/src/cwe_checker_lib/src/utils/ghidra.rs +++ b/src/cwe_checker_lib/src/utils/ghidra.rs @@ -183,7 +183,7 @@ fn generate_ghidra_call_command( .ok_or_else(|| anyhow!("Invalid file name"))? .to_string_lossy() .to_string(); - let ghidra_plugin_path = get_ghidra_plugin_path("p_code_extractor"); + let ghidra_plugin_path = get_ghidra_plugin_path("p_code_extractor")?; let mut ghidra_command = Command::new(headless_path); ghidra_command diff --git a/src/cwe_checker_lib/src/utils/mod.rs b/src/cwe_checker_lib/src/utils/mod.rs index 93beae261..f142781be 100644 --- a/src/cwe_checker_lib/src/utils/mod.rs +++ b/src/cwe_checker_lib/src/utils/mod.rs @@ -10,21 +10,77 @@ pub mod symbol_utils; use crate::prelude::*; +use std::{env, fs, path}; + +use anyhow::bail; + +const ENV_CWE_CHECKER_CONFIGS_PATH: &str = "CWE_CHECKER_CONFIGS_PATH"; +const ENV_CWE_CHECKER_GHIDRA_PLUGINS_PATH: &str = "CWE_CHECKER_GHIDRA_PLUGINS_PATH"; + /// Get the contents of a configuration file. +/// +/// We first search the file in our config directory. Then, we fall back to +/// the CWE_CHECKER_CONFIG environment variable. pub fn read_config_file(filename: &str) -> Result { - let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker") - .context("Could not discern location of configuration files.")?; + let config_path = if let Some(config_path) = get_config_path_from_project_dir(filename) { + config_path + } else if let Some(config_path) = get_path_from_env(ENV_CWE_CHECKER_CONFIGS_PATH, filename) { + config_path + } else { + bail!("Unable to find configuration file: {}.", filename) + }; + let config_file = fs::read_to_string(config_path) + .context(format!("Could not read configuration file: {}", filename))?; + Ok(serde_json::from_str(&config_file)?) +} + +fn get_config_path_from_project_dir(filename: &str) -> Option { + let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker")?; let config_dir = project_dirs.config_dir(); let config_path = config_dir.join(filename); - let config_file = - std::fs::read_to_string(config_path).context("Could not read configuration file")?; - Ok(serde_json::from_str(&config_file)?) + + if config_path.exists() { + Some(config_path) + } else { + None + } +} + +/// Get the path to a Ghidra plugin that is bundled with the cwe_checker. +/// +/// We first search the plugin in our data directory, then we fall back to +/// the CWE_CHECKER_GHIDRA_PLUGIN_PATH environment variable. +pub fn get_ghidra_plugin_path(plugin_name: &str) -> Result { + if let Some(ghidra_plugin_path) = get_ghidra_plugin_path_from_project_dirs(plugin_name) { + Ok(ghidra_plugin_path) + } else if let Some(ghidra_plugin_path) = + get_path_from_env(ENV_CWE_CHECKER_GHIDRA_PLUGINS_PATH, plugin_name) + { + Ok(ghidra_plugin_path) + } else { + bail!("Unable to find Ghidra plugin: {}", plugin_name) + } } -/// Get the folder path to a Ghidra plugin bundled with the cwe_checker. -pub fn get_ghidra_plugin_path(plugin_name: &str) -> std::path::PathBuf { - let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker") - .expect("Could not discern location of data directory."); +fn get_ghidra_plugin_path_from_project_dirs(plugin_name: &str) -> Option { + let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker")?; let data_dir = project_dirs.data_dir(); - data_dir.join("ghidra").join(plugin_name) + let plugin_path = data_dir.join("ghidra").join(plugin_name); + + if plugin_path.exists() { + Some(plugin_path) + } else { + None + } +} + +fn get_path_from_env(var: &str, filename: &str) -> Option { + let val = env::var(var).ok()?; + let path = path::PathBuf::from(val).join(filename); + + if path.exists() { + Some(path) + } else { + None + } }