Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
.vscode
*.profdata
*.profraw
*.swp
target
macros/target
site/node_modules
site/public
site/resources
site/resources
34 changes: 34 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ dsa = "0.6.3"
ecdsa = "0.16.9"
enable-ansi-support = "0.2.1"
env_logger = "0.11.3"
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: 2 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ 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"] }
log = { workspace = true, optional = true }
protobuf = { workspace = true }
protobuf-json-mapping = { workspace = true }
serde_json = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true }
yansi = { workspace = true }
yara-x = { workspace = true, features = ["parallel-compilation"] }
yara-x-parser = { workspace = true }
Expand Down
27 changes: 24 additions & 3 deletions cli/src/commands/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use std::fs::File;
use std::path::PathBuf;
use std::{fs, io, process};

use crate::help::FMT_CHECK_MODE;
use crate::help::{CONFIG_FILE, FMT_CHECK_MODE};
use clap::{arg, value_parser, ArgAction, ArgMatches, Command};
use yara_x::config::{load_config_from_file, FormatConfig};
use yara_x_fmt::Formatter;

pub fn fmt() -> Command {
Expand All @@ -17,13 +18,33 @@ pub fn fmt() -> Command {
.action(ArgAction::Append),
)
.arg(arg!(-c --check "Run in 'check' mode").long_help(FMT_CHECK_MODE))
.arg(
arg!(-C --config <CONFIG_FILE> "Config file")
.value_parser(value_parser!(PathBuf))
.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 config: FormatConfig = if config_file.is_some() {
load_config_from_file(&config_file.unwrap())?.fmt
} else {
main_config
};

let formatter = Formatter::new();
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)
.indent_spaces(config.rule.indent_spaces);
let mut changed = false;

for file in files {
Expand Down
8 changes: 8 additions & 0 deletions cli/src/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,11 @@ 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 CONFIG_FILE: &str = r#"Config file for YARA-X

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, ${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
85 changes: 85 additions & 0 deletions docs/YARA-X Config Guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
YARA-X Config Guide
===================

YARA-X uses a configuration file for controlling the behavior of different
commands. It currently supports the fmt command, but others will be added in the
future.

The `yr` command looks in `${HOME}/.yara-x.toml` when starting up. If that file
does not exist the default values are used.

An example `.yara-x.toml` file is below, with comments that explain each option.
This is the definitive list of supported configuration options, and will be
updated as more are added.

```toml
# Config file for YARA-X.

# Any options that are not valid are ignored. However, valid keys with an
# invalid type will cause a parsing error. For example, if you set
# rule.indent_spaces to false, it will result in a parsing error.
pants = false # Invalid keys are ignored.

# The configuration of the "fmt" subcommand can be controlled by options in the
# "fmt" section. Each line is a key-value pair where the key uses a dot notation
# to deliniate different options. The "rule" namespace are for options that
# apply to the rule as a whole, while the "meta" and "patterns" namespaces are
# for options that only apply to those sections in a rule.
[fmt]
# Indent section headers so that:
#
# rule a {
# condition:
# true
# }
#
# Becomes:
#
# rule a {
# condition:
# true
# }
rule.indent_section_headers = true

# Indent section contents so that:
# rule a {
# condition:
# true
# }
#
# Becomes:
#
# rule a {
# condition:
# true
# }
rule.indent_section_contents = true

# Number of spaces to use for indentation. Setting this to 0 will use one tab
# character per level of indentation. To disable indentation entirely use
# rule.indent_section_headers and rule.indent_section_contents
rule.indent_spaces = 2

# Align metadata values so that:
#
# rule a {
# meta:
# key = "a"
# long_key = "b"
# }
#
# Becomes:
#
# rule a {
# meta:
# key = "a"
# long_key = "b"
# }
#
# Note that alignment is done with spaces, regardless of rule.indent_spaces
# setting.
meta.align_values = false

# Same as meta.align_values but applies to patterns.
patterns.align_values = false
```
21 changes: 16 additions & 5 deletions fmt/src/indentation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@ where
{
input: T,
indent_level: i16,
num_spaces: u8,
output_buffer: VecDeque<Token<'a>>,
}

impl<'a, T> AddIndentationSpaces<'a, T>
where
T: TokenStream<'a>,
{
pub fn new(input: T) -> Self {
Self { input, indent_level: 0, output_buffer: VecDeque::new() }
pub fn new(input: T, num_spaces: u8) -> Self {
Self {
input,
indent_level: 0,
num_spaces: num_spaces,
output_buffer: VecDeque::new(),
}
}
}

Expand Down Expand Up @@ -52,9 +58,14 @@ where
Token::Newline => {
self.output_buffer.push_back(Token::Newline);
for _ in 0..self.indent_level {
// Indent with two spaces per level
self.output_buffer.push_back(Token::Whitespace);
self.output_buffer.push_back(Token::Whitespace);
if self.num_spaces == 0 {
self.output_buffer.push_back(Token::Tab);
} else {
for _ in 0..self.num_spaces {
self.output_buffer
.push_back(Token::Whitespace);
}
}
}
return self.output_buffer.pop_front();
}
Expand Down
Loading
Loading