OpenViSTAI · Agents · Blockchain · Crypto
Menu
OPENVIST RECIPES

Redact selected configuration assignment values with Rust

Rust

Replace every value assigned to an explicit selected key while preserving the order and literal values of unselected assignments.

Rust code excerpt: Redact selected configuration assignment values with Rust. Version 0.1.0. Read the full recipe for code, sources and recorded test scope.

Replace every value assigned to an explicit selected key while preserving the order and literal values of unselected assignments. Uses saved stdin text; never reads the real process environment.

Before you start

  • Save as recipe.rs and compile with Rust 1.99.0 using: rustc --edition=2021 recipe.rs -o recipe
  • Run ./recipe with no arguments and supply the protocol on stdin. The CLI uses only the standard library and reads no files or environment variables.
  • Place unique selector keys before exactly one literal --- line, followed by KEY=value assignments. Exactly empty lines and lines beginning with # are ignored; whitespace is never trimmed.
  • Keys must match ASCII [A-Za-z_][A-Za-z0-9_]{0,63}. Assignments split at the first =, preserving the remainder literally.
  • Example stdin: TOKEN\n---\nTOKEN=example\nMODE=development\n. Here the displayed \n sequences represent LF characters. Output: {"assignments":[{"key":"TOKEN","value":"[REDACTED]"},{"key":"MODE","value":"development"}]} followed by LF.
  • The entire input is validated before successful stdout. Success emits exactly one JSON object plus LF, exits 0, and leaves stderr empty. Input errors emit only error: invalid input plus LF on stderr and exit 2.
  • Assignment order and duplicate assignments are preserved. There are no output key-name arrays to sort; selector membership is stored in deterministic ASCII order.

Build and check

  1. Save the example as recipe.rs.
  2. Prepare input.txt with selectors, a --- separator and literal assignments; use synthetic values while learning.
  3. Run the documented command and inspect the JSON output.
  4. Exit 2 indicates rejected input; no successful JSON result is emitted.

Limits to understand

  • Reads at most 65537 bytes and rejects input exceeding 65536 bytes. Requires strict UTF-8 without a leading BOM.
  • Rejects CR, NUL, all other ASCII controls except LF, and DEL everywhere, including comments. Other Unicode scalar values are allowed in values and comments.
  • Allows at most 100 unique selector records and 100 assignment records. Duplicate selectors are invalid; duplicate assignments count separately.
  • The combined UTF-8 byte length of all original assignment values, including selected values and duplicates, must not exceed 1024 bytes.
  • This is a literal LF-only protocol, not general dotenv syntax. It performs no quoting interpretation, interpolation, whitespace normalization, or automatic secret detection.
  • Only explicitly selected keys are redacted, including when their original values are empty. Unselected values remain in output; the result is not certified safe to publish.
  • Output-write and allocation failures are outside the input-error guarantee.
  • Literal assignment subset only; no quoting rules, export syntax, interpolation, multiline values or general dotenv compatibility.
  • Unselected assignment values are emitted unchanged. Review output before sharing.
  • stdin may block until EOF; input is bounded to 65536 bytes and 100 selector / 100 assignment records. Total assignment-value bytes are limited to 1024.

Run it

rustc --edition=2021 recipe.rs -o recipe
./recipe < input.txt

Example input

TOKEN
---
TOKEN=secret-one
HOST=localhost
TOKEN=secret-two

Expected output

{"assignments":[{"key":"TOKEN","value":"[REDACTED]"},{"key":"HOST","value":"localhost"},{"key":"TOKEN","value":"[REDACTED]"}]}
INSPECT THE SCRIPT

Inspect, copy and run the documented checks.

This browser preview builds code locally. It does not contact an API or run the code.

Default example ready.

Rust example
use std::collections::BTreeSet;
use std::io::{self, Read, Write};

fn valid_key(key: &str) -> bool {
    let bytes = key.as_bytes();
    if bytes.is_empty() || bytes.len() > 64 {
        return false;
    }
    if !(bytes[0].is_ascii_alphabetic() || bytes[0] == b'_') {
        return false;
    }
    bytes[1..]
        .iter()
        .all(|b| b.is_ascii_alphanumeric() || *b == b'_')
}

fn json_string(output: &mut String, text: &str) {
    const HEX: &[u8; 16] = b"0123456789abcdef";
    output.push('"');
    for ch in text.chars() {
        match ch {
            '"' => output.push_str("\\\""),
            '\\' => output.push_str("\\\\"),
            '\u{0000}'..='\u{001f}' => {
                let n = ch as usize;
                output.push_str("\\u00");
                output.push(HEX[n >> 4] as char);
                output.push(HEX[n & 15] as char);
            }
            _ => output.push(ch),
        }
    }
    output.push('"');
}

fn validate_and_render(input: &str) -> Result<String, ()> {
    if input.starts_with('\u{feff}') {
        return Err(());
    }
    if input.bytes().any(|b| (b < 32 && b != b'\n') || b == 127) {
        return Err(());
    }

    let mut selectors: BTreeSet<&str> = BTreeSet::new();
    let mut assignments: Vec<(&str, &str)> = Vec::new();
    let mut separator_seen = false;
    let mut total_value_bytes = 0usize;

    for line in input.split('\n') {
        if line.is_empty() || line.starts_with('#') {
            continue;
        }
        if line == "---" {
            if separator_seen {
                return Err(());
            }
            separator_seen = true;
            continue;
        }
        if !separator_seen {
            if !valid_key(line) || selectors.len() >= 100 {
                return Err(());
            }
            if !selectors.insert(line) {
                return Err(());
            }
        } else {
            if assignments.len() >= 100 {
                return Err(());
            }
            let (key, value) = line.split_once('=').ok_or(())?;
            if !valid_key(key) {
                return Err(());
            }
            total_value_bytes += value.len();
            if total_value_bytes > 1024 {
                return Err(());
            }
            assignments.push((key, value));
        }
    }
    if !separator_seen {
        return Err(());
    }

    // No successful output is constructed until all input is validated.
    let mut output = String::from("{\"assignments\":[");
    for (index, (key, value)) in assignments.iter().enumerate() {
        if index != 0 {
            output.push(',');
        }
        output.push_str("{\"key\":");
        json_string(&mut output, key);
        output.push_str(",\"value\":");
        if selectors.contains(*key) {
            json_string(&mut output, "[REDACTED]");
        } else {
            json_string(&mut output, value);
        }
        output.push('}');
    }
    output.push_str("]}\n");
    Ok(output)
}

fn run() -> Result<String, ()> {
    if std::env::args_os().nth(1).is_some() {
        return Err(());
    }
    let stdin = io::stdin();
    let mut reader = stdin.lock().take(65537);
    let mut bytes = Vec::new();
    reader.read_to_end(&mut bytes).map_err(|_| ())?;
    if bytes.len() > 65536 {
        return Err(());
    }
    let input = std::str::from_utf8(&bytes).map_err(|_| ())?;
    validate_and_render(input)
}

fn main() {
    match run() {
        Ok(output) => {
            let stdout = io::stdout();
            let mut writer = stdout.lock();
            if writer.write_all(output.as_bytes()).is_err() || writer.flush().is_err() {
                std::process::exit(2);
            }
        }
        Err(()) => {
            let stderr = io::stderr();
            let mut writer = stderr.lock();
            let _ = writer.write_all(b"error: invalid input\n");
            let _ = writer.flush();
            std::process::exit(2);
        }
    }
}
What to verify after running
  • Check the supplied example against its expected output.
  • Check all selected duplicate values are replaced; review unselected values before sharing.
  • Confirm invalid syntax and extra arguments fail without input values in diagnostics.

Request or format preview

Illustrative preview. No provider response, price or balance is fetched.

Change a setting to inspect the preview.

Official sources and related profiles

    Code license: MIT. Keep the license notice with your copy.

    Example version 0.1.0 · Documentation reviewed 2026-10-16.

    The release gate requires six primary fixtures and 29 supplementary checks in the pinned offline runtime. Review the bound test evidence for the actual release; these checks do not prove exhaustive correctness or compatibility with other versions.