wallfacer-core 0.4.3

Runtime fuzzing and invariant-testing harness for MCP servers — catch crashes, hangs, schema drift, and state leaks before they ship.
Documentation
use serde_json::{json, Value};

use crate::finding::{Finding, FindingKind, Severity};

pub fn to_sarif(findings: &[Finding], version: &str) -> Value {
    let rules = [
        ("crash", "Server process stopped unexpectedly"),
        ("hang", "Tool call exceeded timeout"),
        ("schema_violation", "Tool response violated output schema"),
        ("property_failure", "Declared property invariant failed"),
        ("protocol_error", "Protocol-level error was observed"),
        (
            "state_leak",
            "State was visible across an expected boundary",
        ),
    ]
    .into_iter()
    .map(|(id, name)| {
        json!({
            "id": id,
            "name": name,
            "shortDescription": { "text": name }
        })
    })
    .collect::<Vec<_>>();

    let results = findings
        .iter()
        .map(|finding| {
            json!({
                "ruleId": rule_id(&finding.kind),
                "level": level(finding.severity),
                "message": { "text": finding.message },
                "properties": {
                    "id": finding.id,
                    "tool": finding.tool,
                    "details": finding.details,
                    "repro": finding.repro
                }
            })
        })
        .collect::<Vec<_>>();

    json!({
        "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
        "version": "2.1.0",
        "runs": [{
            "tool": {
                "driver": {
                    "name": "mcp-wallfacer",
                    "version": version,
                    "informationUri": "https://github.com/lacausecrypto/mcp-wallfacer",
                    "rules": rules
                }
            },
            "results": results
        }]
    })
}

fn rule_id(kind: &FindingKind) -> &'static str {
    match kind {
        FindingKind::Crash => "crash",
        FindingKind::Hang { .. } => "hang",
        FindingKind::SchemaViolation => "schema_violation",
        FindingKind::PropertyFailure { .. } => "property_failure",
        FindingKind::ProtocolError => "protocol_error",
        FindingKind::StateLeak => "state_leak",
        FindingKind::SequenceFailure { .. } => "sequence_failure",
    }
}

fn level(severity: Severity) -> &'static str {
    match severity {
        Severity::Critical | Severity::High => "error",
        Severity::Medium => "warning",
        Severity::Low => "note",
    }
}