forbidden-strings 0.2.0

Out-of-band scanner for forbidden literal strings and regex patterns. Gitignore-aware, fast, dependency-light: built for CI deny-listing of leaked credentials and banned tokens.
//! Build step: precompile the embedded builtin baseline into a serialized `RegexSet`.
//!
//! The migration plan (open-questions resolution) measured that compiling the full
//! ported baseline at scanner startup is not viable: individual faithful rules take
//! seconds to determinize, tens of seconds in the worst case. So the baseline is
//! compiled once here, at build time, and serialized with the engine's `to_bytes`;
//! `lib.rs` embeds the blob with `include_bytes!` and the runtime loader rebuilds it
//! through the stage-one `load_precompiled` (the engine's validating `from_bytes`),
//! which only decodes, never recompiles. Only the small runtime rules files still
//! compile from text at startup.
//!
//! The two-form parse and literal escaper are shared verbatim with the runtime frx
//! compiler by `#[path]`-including the same source files: the build script cannot
//! `use` its own crate, so this is the only way to keep one parser. The engine is a
//! build-dependency so this script can call `RegexSet::new`.

// Scoped to this build-script compilation only. The shared `error.rs` this script
// `#[path]`-includes declares `LoadError::Compile` and `LoadError::Precompiled`, which
// the runtime `frx.rs` constructs but this build step does not (it only calls
// `parse_patterns`). Those variants are dead in the build-script crate yet live in the
// real crate, which keeps `-D dead_code`; suppressing here avoids editing the shared
// stage-one source to satisfy a build-script-only false positive.
#![allow(dead_code)]

/// Imports the engine's compiled-ruleset type built and serialized here.
use forbidden_regex::RegexSet;

/// Imports the environment and filesystem std pieces the build step reads and writes.
use std::{env, fs, path::Path};

/// Registers the redacted load-error type shared with the runtime frx compiler.
///
/// The parser returns it; the build step never renders it with rule text, only its
/// `Display`, whose interpolations are an opaque index and a config flag letter.
#[path = "src/rule/frx/error.rs"]
mod error;

/// Registers the literal-to-verbose-dialect escaper shared with the runtime compiler.
#[path = "src/rule/frx/escape.rs"]
mod escape;

/// Registers the two-form file-format parser and flag policy shared with the runtime.
#[path = "src/rule/frx/format.rs"]
mod format;

/// Compiles the ported baseline once and writes its serialized bytes into `OUT_DIR`.
///
/// Parses the committed, generated `data/builtin-rules.ported.txt` through the shared
/// two-form parser, compiles the whole set through the engine, serializes it, and
/// writes the blob for `include_bytes!`. A parse or compile failure fails the build,
/// which is the correct fail-closed response to a corrupt or un-ported baseline; the
/// error is surfaced only through its redacted `Display`, never rule text.
fn main() {
    // Rerun only when the baseline data or the shared parser sources change; the
    // build script's own edits are tracked by cargo automatically.
    println!("cargo:rerun-if-changed=data/builtin-rules.ported.txt");
    println!("cargo:rerun-if-changed=src/rule/frx/format.rs");
    println!("cargo:rerun-if-changed=src/rule/frx/escape.rs");
    println!("cargo:rerun-if-changed=src/rule/frx/error.rs");

    let manifest_dir =
        env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR set by cargo");
    let out_dir = env::var("OUT_DIR").expect("OUT_DIR set by cargo");

    let ported_path = Path::new(&manifest_dir).join("data/builtin-rules.ported.txt");
    let ported = fs::read_to_string(&ported_path)
        .unwrap_or_else(|error| panic!("read {}: {error}", ported_path.display()));

    // Parse the two-form baseline into engine-ready patterns, then compile the whole
    // set in one pass. `Display` on either error carries no rule text.
    let patterns = format::parse_patterns(&ported)
        .unwrap_or_else(|error| panic!("parse builtin baseline: {error}"));
    let set = RegexSet::new(&patterns)
        .unwrap_or_else(|error| panic!("compile builtin baseline: {error}"));
    let bytes = set
        .to_bytes()
        .unwrap_or_else(|error| panic!("serialize builtin baseline: {error}"));

    let out_path = Path::new(&out_dir).join("builtin-rules-precompiled.bin");
    fs::write(&out_path, bytes)
        .unwrap_or_else(|error| panic!("write {}: {error}", out_path.display()));
}