Skip to content

Commit

Permalink
Improve config file handling.
Browse files Browse the repository at this point in the history
Move the config structures and loading to a separate module, so that it can be
used by each command.

Pass the "fmt" section of the config to the "fmt" command. As we add support for
other commands to use the config file we can easily pass their sub-structure
into the command.

Load the config file from %{HOME}/.yara-x.toml - or whatever the corresponding
location is on Windows.

The exact format of the config is still to be documented, but now that it is
generic and not specific to formatting I've moved the docs to a new file that is
more appropriately named.
  • Loading branch information
wxsBSD committed Sep 29, 2024
1 parent 812afb3 commit 4481fdb
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 71 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ figment = "0.10.19"
fmmap = "0.3.3"
globwalk = "0.9.1"
goldenfile = "1.6.1"
home = "0.5.9"
ihex = "3.0.0"
indenter = "0.3.3"
indexmap = "2.2.6"
Expand Down
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ anyhow = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive"] }
clap_complete = { workspace = true }
globwalk = { workspace = true }
home = { workspace = true }
itertools = { workspace = true }
enable-ansi-support = { workspace = true }
env_logger = { workspace = true, optional = true, features = ["auto-color"] }
figment = { workspace = true, features = ["toml"] }
log = { workspace = true, optional = true }
protobuf = { workspace = true }
protobuf-json-mapping = { workspace = true }
Expand Down
67 changes: 12 additions & 55 deletions cli/src/commands/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,11 @@ use std::fs::File;
use std::path::PathBuf;
use std::{fs, io, process};

use crate::help::{FMT_CHECK_MODE, FMT_CONFIG_FILE};
use crate::help::{CONFIG_FILE, FMT_CHECK_MODE};
use clap::{arg, value_parser, ArgAction, ArgMatches, Command};
use figment::{
providers::{Format, Serialized, Toml},
Figment,
};
use serde::{Deserialize, Serialize};
use yara_x::config::{load_config_from_file, FormatConfig};
use yara_x_fmt::Formatter;

#[derive(Deserialize, Serialize, Debug)]
struct Config {
rule: Rule,
meta: Meta,
patterns: Patterns,
}

#[derive(Deserialize, Serialize, Debug)]
struct Rule {
indent_section_headers: bool,
indent_section_contents: bool,
}

#[derive(Deserialize, Serialize, Debug)]
struct Meta {
align_values: bool,
}

#[derive(Deserialize, Serialize, Debug)]
struct Patterns {
align_values: bool,
}

impl Default for Config {
fn default() -> Config {
Config {
rule: Rule {
indent_section_headers: true,
indent_section_contents: true,
},
meta: Meta { align_values: true },
patterns: Patterns { align_values: true },
}
}
}

pub fn fmt() -> Command {
super::command("fmt")
.about("Format YARA source files")
Expand All @@ -61,29 +21,26 @@ pub fn fmt() -> Command {
.arg(
arg!(-C --config <CONFIG_FILE> "Config file")
.value_parser(value_parser!(PathBuf))
.long_help(FMT_CONFIG_FILE),
.long_help(CONFIG_FILE),
)
}

pub fn exec_fmt(args: &ArgMatches) -> anyhow::Result<()> {
pub fn exec_fmt(args: &ArgMatches, main_config: FormatConfig) -> anyhow::Result<()> {
let files = args.get_many::<PathBuf>("FILE").unwrap();
let check = args.get_flag("check");
let config_file = args.get_one::<PathBuf>("config");

let formatter = if config_file.is_some() {
let config: Config =
Figment::from(Serialized::defaults(Config::default()))
.merge(Toml::file_exact(&config_file.unwrap()))
.extract()?;
Formatter::new()
.align_metadata(config.meta.align_values)
.align_patterns(config.patterns.align_values)
.indent_section_headers(config.rule.indent_section_headers)
.indent_section_contents(config.rule.indent_section_contents)
let config: FormatConfig = if config_file.is_some() {
load_config_from_file(&config_file.unwrap())?.fmt
} else {
Formatter::new()
main_config
};

let formatter = Formatter::new()
.align_metadata(config.meta.align_values)
.align_patterns(config.patterns.align_values)
.indent_section_headers(config.rule.indent_section_headers)
.indent_section_contents(config.rule.indent_section_contents);
let mut changed = false;

for file in files {
Expand Down
7 changes: 4 additions & 3 deletions cli/src/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,10 @@ pub const FMT_CHECK_MODE: &str = r#"Run in 'check' mode
Doesn't modify the files. Exits with 0 if files are formatted correctly. Exits
with 1 if formatting is required."#;

pub const FMT_CONFIG_FILE: &str = r#"Config file for formatting
pub const CONFIG_FILE: &str = r#"Config file for YARA-X
Config file which controls the behavior of the formatter. See XXX (FILL IN URL
Config file which controls the behavior of YARA-X. See XXX (FILL IN URL
ONCE DOCS ARE WRITTEN) for supported options.
If config file is not specified the default formatting options are applied."#;
If config file is not specified, ${HOME}/.yara-x.toml is used. If that does not
exist the default options are applied."#;
21 changes: 20 additions & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ mod help;
mod walk;

use crossterm::tty::IsTty;
use home;
use std::{io, panic, process};
use yansi::Color::Red;
use yansi::Paint;
use yara_x::config::{load_config_from_file, Config};

use crate::commands::cli;

Expand All @@ -19,6 +21,7 @@ const APP_HELP_TEMPLATE: &str = r#"YARA-X {version}, the pattern matching swiss
"#;

const EXIT_ERROR: i32 = 1;
const CONFIG_FILE: &str = ".yara-x.toml";

fn main() -> anyhow::Result<()> {
// Enable support for ANSI escape codes in Windows. In other platforms
Expand Down Expand Up @@ -58,12 +61,28 @@ fn main() -> anyhow::Result<()> {
process::exit(EXIT_ERROR);
}));

let config: Config = match home::home_dir() {
Some(home_path) if !home_path.as_os_str().is_empty() => {
match load_config_from_file(&home_path.join(CONFIG_FILE)) {
Ok(c) => c,
Err(e) => {
println!("Error parsing config, using defaults: {}", e);
Config::default()
}
}
}
_ => {
println!("Unable to find home directory, using defaults.");
Config::default()
}
};

let result = match args.subcommand() {
#[cfg(feature = "debug-cmd")]
Some(("debug", args)) => commands::exec_debug(args),
Some(("check", args)) => commands::exec_check(args),
Some(("fix", args)) => commands::exec_fix(args),
Some(("fmt", args)) => commands::exec_fmt(args),
Some(("fmt", args)) => commands::exec_fmt(args, config.fmt),
Some(("scan", args)) => commands::exec_scan(args),
Some(("dump", args)) => commands::exec_dump(args),
Some(("compile", args)) => commands::exec_compile(args),
Expand Down
File renamed without changes.
10 changes: 0 additions & 10 deletions fmt/src/wxsfmt.toml

This file was deleted.

1 change: 1 addition & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ der-parser = { workspace = true, optional = true, features = ["bigint"] }
digest = { workspace = true, optional = true }
dsa = { workspace = true, optional = true }
ecdsa = { workspace = true, optional = true }
figment = { workspace = true, features = ["toml"] }
fmmap = { workspace = true }
indexmap = { workspace = true, features = ["serde"] }
intaglio = { workspace = true }
Expand Down
75 changes: 75 additions & 0 deletions lib/src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use figment::{
providers::{Format, Serialized, Toml},
Figment,
};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

/// Configuration structure for "yr" commands.
#[derive(Deserialize, Serialize, Debug)]
pub struct Config {
/// Format specific configuration information.
pub fmt: FormatConfig,
}

/// Format specific configuration information.
#[derive(Deserialize, Serialize, Debug)]
pub struct FormatConfig {
/// Rule specific formatting information.
pub rule: Rule,
/// Meta specific formatting information.
pub meta: Meta,
/// Pattern specific formatting information.
pub patterns: Patterns,
}

/// Rule specific formatting information.
#[derive(Deserialize, Serialize, Debug)]
pub struct Rule {
/// Indent section headers (meta, strings, condition).
pub indent_section_headers: bool,
/// Indent section contents one level past section headers.
pub indent_section_contents: bool,
}

/// Meta specific formatting information.
#[derive(Deserialize, Serialize, Debug)]
pub struct Meta {
/// Align values to longest key.
pub align_values: bool,
}

/// Pattern specific formatting information.
#[derive(Deserialize, Serialize, Debug)]
pub struct Patterns {
/// Align patterns to longest name.
pub align_values: bool,
}

impl Default for Config {
fn default() -> Config {
Config {
fmt: FormatConfig {
rule: Rule {
indent_section_headers: true,
indent_section_contents: true,
},
meta: Meta { align_values: true },
patterns: Patterns { align_values: true },
},
}
}
}

/// Load config file from a given path. Path must contain a valid TOML file or
/// this function will propagate the error. For structure of the config file
/// see "YARA-X Config Guide.md".
pub fn load_config_from_file(
config_file: &PathBuf,
) -> Result<Config, figment::Error> {
let config: Config =
Figment::from(Serialized::defaults(Config::default()))
.merge(Toml::file_exact(config_file.as_path()))
.extract()?;
Ok(config)
}
3 changes: 3 additions & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ mod models;
#[cfg(test)]
mod tests;

/// Used to parse and handle YARA-X configuration options.
pub mod config;

pub mod errors {
//! Errors returned by this crate.
//!
Expand Down

0 comments on commit 4481fdb

Please sign in to comment.