secret_scraper 0.1.3

A URL Crawler tool and library for crawling web targets, discovering links, and detecting secrets with configurable regex rules.
Documentation
//! Secret detection handlers and result types.

use anyhow::Result;
use derive_builder::Builder;
use serde::Serialize;

use crate::cli::Rule;

/// A secret detected by a handler.
#[derive(PartialEq, Eq, Hash, Serialize, Builder)]
#[allow(missing_docs)]
pub struct Secret {
    /// Human-readable rule or secret category name.
    pub secret_type: String,
    /// Matched secret data.
    pub data: String,
}
/// Trait implemented by text scanners that emit detected secrets.
pub trait Handler: Send + Sync + 'static {
    /// Scan `text` and return all detected secrets.
    fn handle(&self, text: &str) -> Result<Vec<Secret>>;
}

/// Regex-backed [`Handler`] using configured [`Rule`] values.
pub struct RegexHandler {
    rules: Vec<Rule>,
}
impl RegexHandler {
    /// Build a regex handler from non-empty rules.
    pub fn new(rules: Vec<Rule>) -> Result<Self> {
        (!rules.is_empty())
            .then_some(())
            .ok_or(anyhow::anyhow!("no rule specified"))?;
        Ok(Self { rules })
    }
}
impl Handler for RegexHandler {
    fn handle(&self, text: &str) -> Result<Vec<Secret>> {
        Ok(self
            .rules
            .iter()
            .flat_map(|rule| {
                if rule.group {
                    rule.regex
                        .captures_iter(text)
                        .flat_map(|caps| {
                            caps.iter()
                                .skip(1)
                                .flatten()
                                .map(|m| Secret {
                                    secret_type: rule.name.clone(),
                                    data: m.as_str().to_string(),
                                })
                                .collect::<Vec<_>>()
                        })
                        .collect::<Vec<Secret>>()
                } else {
                    rule.regex
                        .captures_iter(text)
                        .filter_map(|caps| {
                            caps.iter().next().flatten().map(|m| Secret {
                                secret_type: rule.name.clone(),
                                data: m.as_str().to_string(),
                            })
                        })
                        .collect::<Vec<Secret>>()
                }
            })
            .collect())
    }
}