Sign in

    termproof

    Streaming ANSI/ECMA-48 terminal log sanitizer and security auditor for MoonBit

    ansi
    terminal
    security
    logs
    ci
    Download zip
    Author
    Version
    0.1.1
    License
    MIT
    Last updated
    2 months ago
    Downloads
    33

    No dependencies

    #TermProof

    TermProof is a MoonBit library and command-line tool for auditing untrusted terminal logs before they are shown in CI, issue trackers, release notes, or chat systems.

    Terminal output is not plain text. ANSI/ECMA-48 control sequences can move the cursor, erase history, rewrite previous lines, set deceptive hyperlinks, write to the clipboard, or hide filenames with Unicode bidirectional controls. TermProof turns that output into deterministic text and a security report.

    #Why This Project Is Different

    Most terminal packages help programs produce nicer output. TermProof looks at the opposite side: it treats terminal output as hostile input. The project focuses on log integrity, reproducible auditing, and safe rendering instead of colors, TUIs, progress bars, or shell helpers.

    #Features

    • Streaming ECMA-48 parser that handles split ESC, CSI, OSC, DCS, SOS, PM, and APC sequences.
    • Bounded control-string buffering with explicit truncated events for oversized or incomplete input.
    • Classification for cursor movement, screen erase, terminal queries, title changes, OSC 8 hyperlinks, OSC 52 clipboard writes, iTerm-style file transfer extensions, notifications, and device-control strings.
    • Unicode bidirectional-control detection for Trojan Source style filename or source-code disguise.
    • Four built-in policies: strict, ci, plain, and permissive.
    • Safe outputs: plain_text, safe_terminal, JSON, Markdown, and GitHub Actions annotations.
    • Source-location mapping for findings, including line/column positions, caret snippets, and line-risk maps.
    • Risk profiles, rule catalog metadata, remediation plans, and full audit overviews for release-gate workflows.
    • CLI commands for auditing, cleaning, event dumps, and a built-in demo.
    • Original fixtures and unit tests. No third-party log corpus is bundled.

    #Installation

    Install from mooncakes.io:

    moon add DWS-ai-nb/termproof

    Import the library from another package:

    import {
    "DWS-ai-nb/termproof" @termproof
    }

    #Quick Start

    fn main {
    let log = "ok\u{1b}[32mPASS\u{1b}[0m\n" +
    "copy\u{1b}]52;c;SGVsbG8=\u{7}\n"
    let result = @termproof.audit(log, policy=@termproof.Policy::strict())
    println(@termproof.summary_line(result))
    println(@termproof.profile_summary(result.profile()))
    println(@termproof.remediation_summary(result))
    println(result.plain_text)
    }

    #CLI

    Run the built-in example:

    moon run cmd/termproof -- demo

    Run the library examples:

    moon run examples/audit moon run examples/overview

    Audit text and fail when the selected policy threshold is reached:

    moon run cmd/termproof -- audit "trusted output" --policy strict

    Write a Markdown report:

    moon run cmd/termproof -- audit "trusted output" --format markdown

    Clean text for display:

    moon run cmd/termproof -- clean "trusted output" --policy plain

    Emit parser events for debugging:

    moon run cmd/termproof -- events "trusted output"

    Generate a line-aware overview with risk profile and remediation advice:

    moon run cmd/termproof -- overview "trusted output" --format markdown

    The same overview APIs are available from the library:

    let overview = @termproof.audit_overview("safe\nbad\u{1b}[2J")
    println(@termproof.overview_summary(overview))
    println(@termproof.overview_markdown(overview))

    #Policies

    PolicyIntended useKeeps SGR colorsKeeps OSC 8 linksMarks dangerous dataFails at
    strictreview and artifact publishingnonoyesdangerous
    ciCI logs that may keep colorsyesnonocritical
    plainirreversible text exportnononocritical
    permissivediagnostics and migrationyesyes, except high-risk schemesnocritical

    #Supported Scope

    TermProof is intentionally narrow. It parses terminal-control syntax and classifies security-relevant behavior in logs. It does not emulate a full terminal, execute shell commands, render a TUI, or validate every terminal-specific private extension. Unknown and malformed controls are still reported and removed according to policy.

    #Rule Catalog and Release Gates

    Each finding uses a stable rule identifier such as TP-OSC-052 for clipboard writes or TP-UNI-001 for Unicode bidirectional controls. The rule_catalog_* APIs expose titles, categories, explanations, default severities, and safe defaults so downstream tools can render local help without copying tables by hand.

    profile_audit, remediation_plan, and audit_overview build on the raw findings:

    • profile_audit summarizes severity counts, decisions, top risk, rule distribution, and exposure score.
    • remediation_plan converts findings into deduplicated actions and a release gate: pass, observe, review, fix, or block.
    • audit_overview combines the raw audit, source locations, profile, remediation plan, line-risk map, Markdown, JSON, and plain-text report rendering.

    For streaming callers, Parser::new_with_limit(limit) bounds the retained control sequence prefix. When the limit is exceeded, the parser consumes through the terminator and emits a Truncated event instead of growing memory without bound.

    #Development

    moon check --deny-warn moon fmt --check moon build moon test --deny-warn moon run examples/audit moon run cmd/termproof -- audit "trusted output" --policy strict moon info moon package --list

    The GitHub Actions workflow runs the same checks on every push and pull request. The default target is wasm-gc, so the project can build and test without requiring a local C compiler.

    #Publishing

    Version 0.1.0 has been published to mooncakes.io. The current local release metadata is prepared for 0.1.1 after the line-aware overview expansion:

    Release command:

    moon login moon package --list moon publish --frozen

    Mooncakes uses the README and metadata from moon.mod for the package page, so both are kept as part of the release surface.

    #License

    TermProof is released under the MIT License. See LICENSE and THIRD_PARTY_NOTICES.md.

    ActionPriority

    pub(all) enum ActionPriority {
    Observe
    Review
    Fix
    Block
    } derive(Compare, Eq,
    Debug
    )

    ActionPriority::name

    fn ActionPriority::name(self : ActionPriority) -> String

    ActionPriority::rank

    fn ActionPriority::rank(self : ActionPriority) -> Int

    AuditOverview

    pub(all) struct AuditOverview {
    result : AuditResult
    profile : AuditProfile
    remediation : RemediationPlan
    line_index : LineIndex
    located : Array[LocatedFinding]
    line_risks : Array[LineRisk]
    } derive(Eq,
    Debug
    )

    AuditOverview::failed

    fn AuditOverview::failed(self : AuditOverview) -> Bool

    AuditOverview::gate

    fn AuditOverview::gate(self : AuditOverview) -> String

    AuditOverview::passed

    fn AuditOverview::passed(self : AuditOverview) -> Bool

    AuditOverview::summary

    fn AuditOverview::summary(self : AuditOverview) -> String

    AuditProfile

    pub(all) struct AuditProfile {
    total_findings : Int
    total_events : Int
    text_chars : Int
    control_events : Int
    severity : SeverityTally
    decisions : DecisionTally
    risks : Array[RiskTally]
    rules : Array[RuleTally]
    highest_severity : Severity
    score : ProfileScore
    } derive(Eq,
    Debug
    )

    AuditProfile::blocking

    fn AuditProfile::blocking(self : AuditProfile) -> Bool

    AuditProfile::has_critical

    fn AuditProfile::has_critical(self : AuditProfile) -> Bool

    AuditProfile::has_dangerous

    fn AuditProfile::has_dangerous(self : AuditProfile) -> Bool

    AuditProfile::is_clean

    fn AuditProfile::is_clean(self : AuditProfile) -> Bool

    AuditProfile::summary

    fn AuditProfile::summary(self : AuditProfile) -> String

    AuditResult

    pub(all) struct AuditResult {
    plain_text : String
    safe_terminal : String
    events : Array[Event]
    findings : Array[Finding]
    stats : Stats
    failed : Bool
    } derive(Eq,
    Debug
    )

    Full result returned by audit.

    AuditResult::located

    fn AuditResult::located(self : AuditResult, source : String) -> Array[LocatedFinding]

    AuditResult::profile

    fn AuditResult::profile(self : AuditResult) -> AuditProfile

    AuditResult::remediation

    fn AuditResult::remediation(self : AuditResult) -> RemediationPlan

    CsiCommand

    pub(all) struct CsiCommand {
    private_prefix : String
    params : Array[Int?]
    intermediates : String
    final_byte : Char
    } derive(Eq,
    Debug
    )

    Parsed view of a CSI payload. Missing parameters are represented as None; this preserves the semantic difference between an omitted value and zero.

    Decision

    pub(all) enum Decision {
    Keep
    Drop
    Mark
    } derive(Eq,
    Debug
    )

    Action selected by a policy for an event.

    DecisionTally

    pub(all) struct DecisionTally {
    kept : Int
    dropped : Int
    marked : Int
    } derive(Eq,
    Debug
    )

    DecisionTally::total

    fn DecisionTally::total(self : DecisionTally) -> Int

    Event

    pub(all) struct Event {
    kind : EventKind
    raw : String
    payload : String
    final_byte : Char?
    offset : Int
    complete : Bool
    } derive(Eq,
    Debug
    )

    One event emitted by the streaming parser.

    raw contains the original sequence, payload omits introducers and terminators, and final_byte is set for CSI/ESC commands that have one.

    EventKind

    pub(all) enum EventKind {
    Text
    C0
    Escape
    Csi
    Osc
    Dcs
    Sos
    Pm
    Apc
    C1
    Invalid
    Truncated
    } derive(Eq,
    Debug
    )

    Broad syntactic family of an ECMA-48 event.

    ExposureLevel

    pub(all) enum ExposureLevel {
    Clean
    Low
    Medium
    High
    Severe
    } derive(Compare, Eq,
    Debug
    )

    ExposureLevel::description

    fn ExposureLevel::description(self : ExposureLevel) -> String

    ExposureLevel::name

    fn ExposureLevel::name(self : ExposureLevel) -> String

    Finding

    pub(all) struct Finding {
    rule_id : String
    severity : Severity
    risk : RiskKind
    message : String
    offset : Int
    sequence : String
    decision : Decision
    } derive(Eq,
    Debug
    )

    A human-readable security finding tied to an event offset.

    LineIndex

    pub(all) struct LineIndex {
    source : String
    lines : Array[LineSlice]
    chars : Int
    } derive(Eq,
    Debug
    )

    LineIndex::caret_at

    fn LineIndex::caret_at(self : LineIndex, offset : Int) -> String

    LineIndex::is_empty

    fn LineIndex::is_empty(self : LineIndex) -> Bool

    LineIndex::line_at

    fn LineIndex::line_at(self : LineIndex, line : Int) -> LineSlice?

    LineIndex::line_count

    fn LineIndex::line_count(self : LineIndex) -> Int

    LineIndex::line_for_offset

    fn LineIndex::line_for_offset(self : LineIndex, offset : Int) -> LineSlice

    LineIndex::line_preview

    fn LineIndex::line_preview(self : LineIndex, line : Int) -> String

    LineIndex::line_with_number

    fn LineIndex::line_with_number(self : LineIndex, line : Int) -> String

    LineIndex::position_at

    fn LineIndex::position_at(self : LineIndex, offset : Int) -> SourcePosition

    LineIndex::range_for_event

    fn LineIndex::range_for_event(self : LineIndex, event : Event) -> SourceRange

    LineIndex::range_for_finding

    fn LineIndex::range_for_finding(self : LineIndex, finding : Finding) -> SourceRange

    LineIndex::window_at

    fn LineIndex::window_at(self : LineIndex, offset : Int, context? : Int) -> String

    LineRisk

    pub(all) struct LineRisk {
    line : Int
    findings : Int
    info : Int
    warnings : Int
    dangerous : Int
    critical : Int
    risks : Array[RiskKind]
    } derive(Eq,
    Debug
    )

    LineRisk::highest

    fn LineRisk::highest(self : LineRisk) -> Severity

    LineSlice

    pub(all) struct LineSlice {
    line : Int
    start_offset : Int
    end_offset : Int
    text : String
    } derive(Eq,
    Debug
    )

    LocatedFinding

    pub(all) struct LocatedFinding {
    finding : Finding
    position : SourcePosition
    line_text : String
    caret : String
    } derive(Eq,
    Debug
    )

    LocatedFinding::markdown_row

    fn LocatedFinding::markdown_row(self : LocatedFinding) -> String

    LocatedFinding::summary

    fn LocatedFinding::summary(self : LocatedFinding) -> String

    OscCommand

    pub(all) struct OscCommand {
    command : String
    data : String
    } derive(Eq,
    Debug
    )

    Parsed view of an OSC event.

    Parser

    pub(all) struct Parser {
    mode : ParserMode
    text : Array[Char]
    sequence : Array[Char]
    payload : Array[Char]
    sequence_limit : Int
    sequence_truncated : Bool
    sequence_start : Int
    offset : Int
    }

    Incremental ECMA-48 parser. It accepts arbitrary chunk boundaries, including a split between ESC and \\ in an ST terminator.

    Parser::consumed

    fn Parser::consumed(self : Parser) -> Int

    Parser::current_mode

    fn Parser::current_mode(self : Parser) -> ParserMode

    Parser::feed

    fn Parser::feed(self : Parser, chunk : String) -> Array[Event]

    Feed a chunk and return all events completed by that chunk. Text is held until a control boundary or finish, so adjacent chunks coalesce naturally.

    Parser::finish

    fn Parser::finish(self : Parser) -> Array[Event]

    Flush remaining text and turn an unfinished control string into a Truncated event. Calling finish resets the parser to Ground and is idempotent.

    Parser::new

    fn Parser::new() -> Parser

    Parser::new_with_limit

    fn Parser::new_with_limit(limit : Int) -> Parser

    Creates an incremental parser with a bounded control-sequence buffer.

    When a sequence exceeds limit, the parser keeps only the prefix, consumes the rest until its terminator, and emits a Truncated event. This keeps untrusted input from forcing unbounded memory growth while preserving the state-machine boundary for the following text.

    ParserMode

    pub(all) enum ParserMode {
    Ground
    EscapeStart
    EscapeIntermediate
    CsiEntry
    CsiParam
    CsiIntermediate
    OscString
    OscEscape
    DcsString
    DcsEscape
    SosString
    SosEscape
    PmString
    PmEscape
    ApcString
    ApcEscape
    } derive(Eq,
    Debug
    )

    Parser mode that is exposed so callers can inspect streaming state.

    Policy

    pub(all) struct Policy {
    name : String
    keep_sgr : Bool
    keep_hyperlinks : Bool
    keep_bell : Bool
    mark_dangerous : Bool
    remove_bidi : Bool
    fail_at : Severity
    unknown_severity : Severity
    max_sequence_chars : Int
    } derive(Eq,
    Debug
    )

    Sanitization and CI failure policy.

    Policy::ci

    fn Policy::ci() -> Policy

    Policy::permissive

    fn Policy::permissive() -> Policy

    Policy::plain

    fn Policy::plain() -> Policy

    Policy::strict

    fn Policy::strict() -> Policy

    ProfileScore

    pub(all) struct ProfileScore {
    points : Int
    level : ExposureLevel
    label : String
    } derive(Eq,
    Debug
    )

    Remediation

    pub(all) struct Remediation {
    rule_id : String
    risk : RiskKind
    severity : Severity
    priority : ActionPriority
    title : String
    why : String
    immediate : String
    safe_default : String
    long_term : String
    } derive(Eq,
    Debug
    )

    Remediation::detail

    fn Remediation::detail(self : Remediation) -> String

    Remediation::one_line

    fn Remediation::one_line(self : Remediation) -> String

    RemediationPlan

    pub(all) struct RemediationPlan {
    summary : String
    gate : String
    blocked : Bool
    actions : Array[Remediation]
    next_steps : Array[String]
    } derive(Eq,
    Debug
    )

    RemediationPlan::action_count

    fn RemediationPlan::action_count(self : RemediationPlan) -> Int

    RemediationPlan::has_action_for_risk

    fn RemediationPlan::has_action_for_risk(self : RemediationPlan, risk : RiskKind) -> Bool

    RemediationPlan::has_action_for_rule

    fn RemediationPlan::has_action_for_rule(self : RemediationPlan, rule_id : String) -> Bool

    RemediationPlan::strongest_priority

    fn RemediationPlan::strongest_priority(self : RemediationPlan) -> ActionPriority

    RiskKind

    pub(all) enum RiskKind {
    Styling
    CursorMovement
    ScreenRewrite
    ScreenErase
    TerminalQuery
    ModeChange
    TitleChange
    Hyperlink
    ClipboardWrite
    Notification
    WorkingDirectory
    FileTransfer
    DeviceControl
    BidirectionalText
    UnknownControl
    MalformedSequence
    } derive(Eq,
    Debug
    )

    Security-relevant behavior inferred from a control sequence.

    RiskTally

    pub(all) struct RiskTally {
    risk : RiskKind
    count : Int
    info : Int
    warnings : Int
    dangerous : Int
    critical : Int
    kept : Int
    dropped : Int
    marked : Int
    first_offset : Int
    last_offset : Int
    } derive(Eq,
    Debug
    )

    RiskTally::highest

    fn RiskTally::highest(self : RiskTally) -> Severity

    RuleCatalogEntry

    pub(all) struct RuleCatalogEntry {
    rule_id : String
    risk : RiskKind
    default_severity : Severity
    category : String
    title : String
    explanation : String
    safe_default : String
    } derive(Eq,
    Debug
    )

    RuleTally

    pub(all) struct RuleTally {
    rule_id : String
    risk : RiskKind
    count : Int
    info : Int
    warnings : Int
    dangerous : Int
    critical : Int
    kept : Int
    dropped : Int
    marked : Int
    first_offset : Int
    last_offset : Int
    } derive(Eq,
    Debug
    )

    RuleTally::highest

    fn RuleTally::highest(self : RuleTally) -> Severity

    Severity

    pub(all) enum Severity {
    Info
    Warning
    Dangerous
    Critical
    } derive(Compare, Eq,
    Debug
    )

    Severity assigned to a terminal-control finding.

    SeverityTally

    pub(all) struct SeverityTally {
    info : Int
    warnings : Int
    dangerous : Int
    critical : Int
    } derive(Eq,
    Debug
    )

    SeverityTally::highest

    fn SeverityTally::highest(self : SeverityTally) -> Severity

    SeverityTally::total

    fn SeverityTally::total(self : SeverityTally) -> Int

    SourcePosition

    pub(all) struct SourcePosition {
    line : Int
    column : Int
    offset : Int
    } derive(Eq,
    Debug
    )

    SourcePosition::compact

    fn SourcePosition::compact(self : SourcePosition) -> String

    SourcePosition::format

    fn SourcePosition::format(self : SourcePosition) -> String

    SourceRange

    pub(all) struct SourceRange {
    start : SourcePosition
    end : SourcePosition
    } derive(Eq,
    Debug
    )

    SourceRange::format

    fn SourceRange::format(self : SourceRange) -> String

    Stats

    pub(all) struct Stats {
    input_chars : Int
    text_chars : Int
    control_events : Int
    info : Int
    warnings : Int
    dangerous : Int
    critical : Int
    dropped : Int
    marked : Int
    } derive(Eq,
    Debug
    )

    Summary counters for an audit run.

    action_priority_name

    fn action_priority_name(priority : ActionPriority) -> String

    audit

    fn audit(input : String, policy? : Policy) -> AuditResult

    Parse, classify, and sanitize terminal output according to policy.

    plain_text never contains terminal controls. safe_terminal may retain SGR or OSC 8 only when explicitly allowed by the selected policy.

    audit_overview

    fn audit_overview(source : String, policy? : Policy) -> AuditOverview

    audit_overview_json

    fn audit_overview_json(source : String, policy? : Policy) -> String

    audit_overview_markdown

    fn audit_overview_markdown(source : String, policy? : Policy) -> String

    audit_overview_text

    fn audit_overview_text(source : String, policy? : Policy) -> String

    build_line_index

    fn build_line_index(source : String) -> LineIndex

    caret_for_column

    fn caret_for_column(column : Int) -> String

    classify

    fn classify(event : Event, policy : Policy) -> Finding?

    compact_location_string

    fn compact_location_string(source : String, offset : Int) -> String

    compact_position

    fn compact_position(position : SourcePosition) -> String

    contains_char

    fn contains_char(input : String, needle : Char) -> Bool

    csi_param

    fn csi_param(command : CsiCommand, index : Int, default : Int) -> Int

    event_kind_name

    fn event_kind_name(kind : EventKind) -> String

    exposure_level_description

    fn exposure_level_description(level : ExposureLevel) -> String

    exposure_level_name

    fn exposure_level_name(level : ExposureLevel) -> String

    finding_catalog_entry

    fn finding_catalog_entry(finding : Finding) -> RuleCatalogEntry?

    finding_policy_note

    fn finding_policy_note(finding : Finding) -> String

    finding_remediation_text

    fn finding_remediation_text(finding : Finding) -> String

    format_position

    fn format_position(position : SourcePosition) -> String

    github_message_escape

    fn github_message_escape(input : String) -> String

    github_property_escape

    fn github_property_escape(input : String) -> String

    GitHub workflow command escaping as documented for annotation properties.

    has_terminal_controls

    fn has_terminal_controls(input : String) -> Bool

    is_bidi_control

    fn is_bidi_control(c : Char) -> Bool

    is_suspicious_uri

    fn is_suspicious_uri(uri : String) -> Bool

    json_quote

    fn json_quote(input : String) -> String

    line_index_summary

    fn line_index_summary(index : LineIndex) -> String

    line_window

    fn line_window(source : String, offset : Int, context? : Int) -> String

    locate_findings

    fn locate_findings(source : String, findings : Array[Finding]) -> Array[LocatedFinding]

    located_findings_json

    fn located_findings_json(located : Array[LocatedFinding]) -> String

    located_findings_markdown

    fn located_findings_markdown(located : Array[LocatedFinding]) -> String

    located_findings_text

    fn located_findings_text(located : Array[LocatedFinding]) -> String

    location_string

    fn location_string(source : String, offset : Int) -> String

    lower_ascii

    fn lower_ascii(input : String) -> String

    markdown_escape

    fn markdown_escape(input : String) -> String

    overview_from_result

    fn overview_from_result(source : String, result : AuditResult) -> AuditOverview

    overview_json

    fn overview_json(overview : AuditOverview) -> String

    overview_markdown

    fn overview_markdown(overview : AuditOverview) -> String

    overview_summary

    fn overview_summary(overview : AuditOverview) -> String

    overview_text

    fn overview_text(overview : AuditOverview) -> String

    parse

    fn parse(input : String) -> Array[Event]

    Convenience batch parser.

    parse_csi

    fn parse_csi(event : Event) -> CsiCommand?

    parse_osc

    fn parse_osc(event : Event) -> OscCommand?

    policy_named

    fn policy_named(name : String) -> Policy?

    profile_audit

    fn profile_audit(result : AuditResult) -> AuditProfile

    profile_has_risk

    fn profile_has_risk(profile : AuditProfile, risk : RiskKind) -> Bool

    profile_has_rule

    fn profile_has_rule(profile : AuditProfile, rule_id : String) -> Bool

    profile_json

    fn profile_json(profile : AuditProfile) -> String

    profile_markdown

    fn profile_markdown(profile : AuditProfile) -> String

    profile_release_gate

    fn profile_release_gate(profile : AuditProfile) -> String

    profile_short_advice

    fn profile_short_advice(profile : AuditProfile) -> String

    profile_summary

    fn profile_summary(profile : AuditProfile) -> String

    profile_text

    fn profile_text(profile : AuditProfile) -> String

    remediation_for_finding

    fn remediation_for_finding(finding : Finding) -> Remediation

    remediation_gate

    fn remediation_gate(result : AuditResult) -> String

    remediation_json

    fn remediation_json(plan : RemediationPlan) -> String

    remediation_markdown

    fn remediation_markdown(plan : RemediationPlan) -> String

    remediation_plan

    fn remediation_plan(result : AuditResult) -> RemediationPlan

    remediation_summary

    fn remediation_summary(result : AuditResult) -> String

    remediation_text

    fn remediation_text(plan : RemediationPlan) -> String

    report_github_annotations

    fn report_github_annotations(result : AuditResult, file? : String) -> String

    report_json

    fn report_json(result : AuditResult, include_events? : Bool) -> String

    Deterministic JSON report with no dependency on a JSON serialization package.

    report_markdown

    fn report_markdown(result : AuditResult) -> String

    risk_count

    fn risk_count(profile : AuditProfile, risk : RiskKind) -> Int

    risk_name

    fn risk_name(risk : RiskKind) -> String

    risk_tally

    fn risk_tally(profile : AuditProfile, risk : RiskKind) -> RiskTally?

    rule_catalog

    fn rule_catalog() -> Array[RuleCatalogEntry]

    rule_catalog_count

    fn rule_catalog_count() -> Int

    rule_catalog_find

    fn rule_catalog_find(rule_id : String) -> RuleCatalogEntry?

    rule_catalog_for_category

    fn rule_catalog_for_category(category : String) -> Array[RuleCatalogEntry]

    rule_catalog_for_risk

    fn rule_catalog_for_risk(risk : RiskKind) -> Array[RuleCatalogEntry]

    rule_catalog_json

    fn rule_catalog_json() -> String

    rule_catalog_markdown

    fn rule_catalog_markdown() -> String

    rule_catalog_safe_default

    fn rule_catalog_safe_default(rule_id : String) -> String

    rule_catalog_text

    fn rule_catalog_text() -> String

    rule_catalog_title

    fn rule_catalog_title(rule_id : String) -> String

    rule_count

    fn rule_count(profile : AuditProfile, rule_id : String) -> Int

    rule_tally

    fn rule_tally(profile : AuditProfile, rule_id : String) -> RuleTally?

    sanitize

    fn sanitize(input : String) -> String

    sanitize_for_ci

    fn sanitize_for_ci(input : String) -> String

    severity_name

    fn severity_name(severity : Severity) -> String

    severity_rank

    fn severity_rank(severity : Severity) -> Int

    should_block_release

    fn should_block_release(result : AuditResult) -> Bool

    starts_with_ascii_case_insensitive

    fn starts_with_ascii_case_insensitive(input : String, prefix : String) -> Bool

    strip_bidi_controls

    fn strip_bidi_controls(input : String) -> String

    summary_line

    fn summary_line(result : AuditResult) -> String

    top_line_risk

    fn top_line_risk(overview : AuditOverview) -> LineRisk?

    top_risk

    fn top_risk(profile : AuditProfile) -> RiskTally?

    top_rule

    fn top_rule(profile : AuditProfile) -> RuleTally?

    visible

    fn visible(input : String) -> String

    Render non-printing characters as stable ASCII. The result is safe to paste into terminals and useful in audit reports.