guardy 0.2.4

Fast, secure git hooks in Rust with secret scanning and protected file synchronization
Documentation
use std::sync::Arc;

use crate::{cli::commands::scan::ScanArgs, config_build, scan::types::ScanMode};

config_build! {
    ScannerConfig<crate::cli::commands::scan::ScanArgs> {
        // Core scanning configuration
        mode: ScanMode => {
            cli: |args: &ScanArgs| args.mode,
            default: ScanMode::Auto,
        },

        // Performance settings
        max_threads: u16 => {
            env: "GUARDY_SCAN_MAX_THREADS",
            default: 0, // 0 = auto-detect
        },

        max_file_size_mb: u32 => {
            cli: |args: &ScanArgs| args.max_file_size.map(|size| size as u32),
            env: "GUARDY_SCAN_MAX_SIZE",
            default: 10,
        },

        thread_percentage: u8 => {
            env: "GUARDY_SCAN_THREAD_PCT",
            default: 70, // Now safe with streaming architecture - no memory accumulation
        },

        stack_size_mb: u32 => {
            env: "GUARDY_SCAN_STACK_SIZE_MB",
            default: 32, // 32MB stack size to prevent overflow on large files
        },

        channel_buffer_multiplier: u32 => {
            env: "GUARDY_SCAN_BUFFER_MULTIPLIER",
            default: 4, // 4x worker count for channel buffers (reduced memory usage)
        },

        result_chunk_size: usize => {
            env: "GUARDY_SCAN_CHUNK_SIZE",
            default: 100, // Smaller chunks to reduce memory pressure
        },

        min_files_for_parallel: usize => {
            env: "GUARDY_SCAN_MIN_FILES_PARALLEL",
            default: 100,
        },

        // File processing options
        include_binary: bool => {
            cli: |args: &ScanArgs| args.include_binary,
            env: "GUARDY_SCAN_INCLUDE_BINARY",
            default: false,
        },

        follow_symlinks: bool => {
            cli: |args: &ScanArgs| args.follow_symlinks,
            env: "GUARDY_SCAN_FOLLOW_SYMLINKS",
            default: false,
        },

        // Entropy analysis configuration
        enable_entropy_analysis: bool => {
            cli: |args: &ScanArgs| args.no_entropy.map(|no_entropy| !no_entropy),
            env: "GUARDY_SCAN_ENTROPY_ENABLED",
            default: true,
        },

        entropy_threshold: f32 => {
            cli: |args: &ScanArgs| args.entropy_threshold.map(|t| t as f32),
            env: "GUARDY_SCAN_ENTROPY_THRESHOLD",
            default: 0.00001,
        },

        // Test code filtering
        ignore_test_code: bool => {
            env: "GUARDY_SCAN_IGNORE_TEST_CODE",
            default: true,
        },

        // Display options
        show: bool => {
            cli: |args: &ScanArgs| args.show,
            env: "GUARDY_SCAN_SHOW",
            default: false,
        },

        sensitive: bool => {
            cli: |args: &ScanArgs| args.sensitive,
            env: "GUARDY_SCAN_SENSITIVE",
            default: false,
        },

        tty: bool => {
            cli: |args: &ScanArgs| args.tty,
            env: "GUARDY_SCAN_TTY",
            default: true,
        },

        // Report output
        report: Option<String> => {
            cli: |args: &ScanArgs| args.report.clone(),
            default: None,
        },

        // Dynamic collections - these need to be built with CLI args
        ignore_paths: Arc<Vec<String>> => {
            cli: |args: &ScanArgs| {
                if args.ignore_paths.is_empty() {
                    None
                } else {
                    let mut paths = vec![
                        "target/".into(),
                        "node_modules/".into(),
                        ".git/".into(),
                        "dist/".into(),
                        "build/".into(),
                    ];
                    paths.extend(args.ignore_paths.clone());
                    Some(Arc::new(paths))
                }
            },
            default: Arc::new(vec![
                "target/".into(),
                "node_modules/".into(),
                ".git/".into(),
                "dist/".into(),
                "build/".into(),
            ]),
        },

        ignore_patterns: Arc<Vec<String>> => {
            cli: |args: &ScanArgs| {
                if args.ignore_patterns.is_empty() {
                    None
                } else {
                    let mut patterns = vec![
                        r#"(?i)password\s*[:=]\s*['"][^'"]*['"]"#.into(),
                        r#"(?i)secret\s*[:=]\s*['"][^'"]*['"]"#.into(),
                    ];
                    patterns.extend(args.ignore_patterns.clone());
                    Some(Arc::new(patterns))
                }
            },
            default: Arc::new(vec![
                r#"(?i)password\s*[:=]\s*['"][^'"]*['"]"#.into(),
                r#"(?i)secret\s*[:=]\s*['"][^'"]*['"]"#.into(),
            ]),
        },

        ignore_comments: Arc<Vec<String>> => {
            cli: |args: &ScanArgs| {
                if args.ignore_comments.is_empty() {
                    None
                } else {
                    let mut comments = vec![
                        "TODO:".into(),
                        "FIXME:".into(),
                        "NOTE:".into(),
                    ];
                    comments.extend(args.ignore_comments.clone());
                    Some(Arc::new(comments))
                }
            },
            default: Arc::new(vec![
                "TODO:".into(),
                "FIXME:".into(),
                "NOTE:".into(),
            ]),
        },

        custom_patterns: Arc<Vec<String>> => {
            cli: |args: &ScanArgs| {
                if args.custom_patterns.is_empty() {
                    None
                } else {
                    Some(Arc::new(args.custom_patterns.clone()))
                }
            },
            default: Arc::new(vec![]),
        },

        test_attributes: Arc<Vec<String>> => {
            default: Arc::new(vec![
                // Rust
                "#[*test]".into(),
                "#[bench]".into(),
                "#[cfg(test)]".into(),
                // Python
                "def test_*".into(),
                "class Test*".into(),
                "@pytest.*".into(),
                // TypeScript/JavaScript
                "it(*".into(),
                "test(*".into(),
                "describe(*".into(),
            ]),
        },

        test_modules: Arc<Vec<String>> => {
            default: Arc::new(vec![
                // Rust
                "mod tests {".into(),
                "mod test {".into(),
                // Python
                "class Test".into(),
                // TypeScript/JavaScript
                "describe(".into(),
                "__tests__".into(),
            ]),
        },
    }
}

// All functionality is now built into the macro-generated ScannerConfig