droidsaw 2.0.0

DROIDSAW — unified Android reverse engineering CLI. Hermes, DEX, APK signing. JSON output, MCP server. Bytecode is not a security layer.
Documentation
use droidsaw_apk::Manifest;

use crate::context::CrossLayerContext;

/// Dump every string from every layer to stdout, one per line. This
/// is the single JSON-exempt command — trufflehog's filesystem mode
/// wants a plain-text feed and we honor its input contract. Do not
/// add a JSON mode; agents that want structured strings call
/// `droidsaw strings` instead.
pub fn trufflehog(
    ctx: &CrossLayerContext,
    min_length: usize,
    out: &mut dyn std::io::Write,
) -> anyhow::Result<()> {
    if let Some(hbc_owned) = ctx.hbc.as_ref() {
        let hbc = hbc_owned.hbc();
        for i in 0..hbc.string_count {
            let s = hbc.string_as_str_or_empty(i);
            if s.len() >= min_length {
                writeln!(out, "{s}")?;
            }
        }
    }

    for dex in &ctx.dex {
        for entry in &dex.strings {
            // Trufflehog scans for credential-pattern matches; use
            // the lossy display view (preserves current behavior
            // including any U+FFFD-substituted entries). Future work:
            // scan `entry.raw_bytes()` via a bytes-aware regex for
            // strict detection — tracked separately.
            let s = entry.as_str_lossy();
            if s.len() >= min_length {
                writeln!(out, "{s}")?;
            }
        }
    }

    if let Some(apk) = ctx.apk.as_ref()
        && let Some(ref raw) = apk.manifest_raw
        && let Ok(m) = Manifest::from_binary_xml(raw)
    {
        if m.package.len() >= min_length {
            writeln!(out, "{}", m.package)?;
        }
        for p in &m.permissions {
            if p.len() >= min_length {
                writeln!(out, "{p}")?;
            }
        }
    }

    Ok(())
}