secreport 0.1.0

Output formatters for security findings — JSON, JSONL, SARIF, Markdown, Text
Documentation

secreport

License Tests Crates.io

Render security findings into text, JSON, JSONL, SARIF, and Markdown.

Why

Every scanner outputs findings in a different shape, but operators need consistent output for dashboards, tickets, and review. secreport standardizes that final step with pluggable formats: text, JSON, JSONL, SARIF, and Markdown.

It accepts both the canonical secfinding::Finding and any type that implements Reportable, so you can keep custom data models while still producing unified artifacts.

Quick Start

use secfinding::{Finding, Severity};
use secreport::{render, emit, Format};

fn main() -> std::io::Result<()> {
    let findings = vec![
        Finding::new(
            "santh-cli",
            "https://example.com/login",
            Severity::High,
            "Potential SQL injection",
            "Untrusted input reaches query context",
        ),
    ];

    let text = render(&findings, Format::Text, "demo-scan");
    emit(&text, None)
}

Features

  • Shared render entry points: render for Finding, render_any for any Reportable type.
  • Formats: Text, Json, Jsonl, Sarif, Markdown.
  • emit for stdout/file emission in one call.
  • Human-friendly colorized text summaries plus severity breakdown.
  • Reusable with any type implementing Reportable, including custom rule_id, CWE/CVE lists, confidence, tags, and exploit hints.

TOML Configuration

secreport does not use TOML configuration.

API Overview

  • Format: output format selector.
  • render: format Vec<Finding>.
  • render_any: format generic Reportable types.
  • emit: print to stdout or write file path.

Examples

1) Produce JSON for API pipelines

use secfinding::Finding;
use secreport::{render, Format};

let findings: Vec<Finding> = Vec::new();
let json = render(&findings, Format::Json, "scanner");
println!("{}", json);

2) Build Markdown report for issue triage

use secfinding::{Finding, Severity};
use secreport::{emit, render, Format};

let findings = vec![
    Finding::new("scanner", "https://target", Severity::Critical, "RCE", "eval on tainted input")
];

let md = render(&findings, Format::Markdown, "nightly-run");
emit(&md, Some("./run-report.md")).unwrap();

3) Render your own finding type

use secreport::{render_any, Format};
use secfinding::{Reportable, Severity};

struct CustomFinding {
    target: String,
    score: f64,
}

impl Reportable for CustomFinding {
    fn scanner(&self) -> &str { "custom" }
    fn target(&self) -> &str { &self.target }
    fn severity(&self) -> Severity { if self.score > 0.8 { Severity::High } else { Severity::Low } }
    fn title(&self) -> &str { "Custom rule" }
}

let findings = vec![CustomFinding { target: "https://example.com".into(), score: 0.93 }];
let out = render_any(&findings, Format::Text, "custom-scan");
println!("{out}");

4) Run the bundled examples

cargo run --example basic
cargo run --example custom_reportable

Traits

secreport does not define traits, but your reporting integration usually depends on secfinding::Reportable (rendered through render_any).

Related Crates

License

MIT, Corum Collective LLC

Docs: https://docs.rs/secreport

Santh ecosystem: https://santh.io