keyhog-scanner 0.5.50

keyhog-scanner: high-performance SIMD-accelerated secret detection engine
Documentation
mod support;

use keyhog_core::{Chunk, ChunkMetadata, RawMatch};
use keyhog_scanner::telemetry::{reset_for_scan, structured_parse_failure_count};
use keyhog_scanner::{CompiledScanner, ScanBackend};
use support::paths::detector_dir;

fn scanner() -> CompiledScanner {
    let detectors = keyhog_core::load_detectors(&detector_dir()).expect("load detectors");
    CompiledScanner::compile(detectors).expect("compile scanner")
}

fn scan(scanner: &CompiledScanner, body: &str, path: &str) -> Vec<RawMatch> {
    let chunk = Chunk {
        data: body.into(),
        metadata: ChunkMetadata {
            source_type: "filesystem".into(),
            path: Some(path.into()),
            ..Default::default()
        },
    };
    scanner.clear_fragment_cache();
    scanner
        .scan_chunks_with_backend(std::slice::from_ref(&chunk), ScanBackend::CpuFallback)
        .expect("selected backend scan succeeds")
        .into_iter()
        .flatten()
        .collect()
}

fn assert_alias_yaml_is_bounded_and_visible(
    scanner: &CompiledScanner,
    body: &str,
    path: &str,
    description: &str,
) {
    reset_for_scan();
    let matches = scan(scanner, body, path);
    assert!(
        matches.is_empty(),
        "{description} must fail closed without findings"
    );
    assert!(
        structured_parse_failure_count() > 0,
        "{description} rejection must be operator-visible as a structured parse failure"
    );
}

#[test]
fn structured_yaml_alias_rejections_are_bounded_and_visible() {
    let scanner = scanner();
    let compose_cycle = "\
services:
  web:
    environment: &env_anchor
      - *env_anchor
";
    assert_alias_yaml_is_bounded_and_visible(
        &scanner,
        compose_cycle,
        "/repo/docker-compose.yaml",
        "cyclic compose YAML aliases",
    );

    let compose_expansion = "\
services:
  web:
    x0: &x0 [lol,lol,lol,lol,lol,lol,lol,lol,lol]
    x1: &x1 [*x0,*x0,*x0,*x0,*x0,*x0,*x0,*x0,*x0]
    x2: &x2 [*x1,*x1,*x1,*x1,*x1,*x1,*x1,*x1,*x1]
    x3: &x3 [*x2,*x2,*x2,*x2,*x2,*x2,*x2,*x2,*x2]
    x4: &x4 [*x3,*x3,*x3,*x3,*x3,*x3,*x3,*x3,*x3]
    environment: *x4
";
    assert_alias_yaml_is_bounded_and_visible(
        &scanner,
        compose_expansion,
        "/repo/docker-compose.yaml",
        "alias-expanded compose YAML",
    );

    let k8s_cycle = "\
apiVersion: v1
kind: Secret
data: &data_anchor
  token: *data_anchor
";
    assert_alias_yaml_is_bounded_and_visible(
        &scanner,
        k8s_cycle,
        "/repo/secret.yaml",
        "cyclic k8s Secret YAML aliases",
    );
}