termsafe

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package termsafe holds the one canonical sanitiser for a string built from untrusted content (commit subjects, refs, file paths, repo prose, error text echoing a malformed file) before it is written to a terminal or a human report. It is the single primitive every render path shares — the terminal analogue of fsutil's guarded read: a hostile or archived repository controls this text, and left raw it can spoof, corrupt, or visually rewrite the report.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CleanProse

func CleanProse(s string, capBytes int) string

CleanProse neutralises one untrusted prose field and caps it at capBytes, preserving interior whitespace runs. The cap is applied last and the result is re-trimmed, so a cut landing mid-word leaves no dangling space; a cut landing mid-rune drops the partial rune rather than emitting replacement bytes.

func CleanProseLine

func CleanProseLine(s string, capBytes int) string

CleanProseLine is CleanProse for a field that must occupy exactly one line: every whitespace run collapses to a single space before the cap is applied. Use it wherever the prose lands in a file whose line structure is machine-read.

func EncodeHiddenRunes added in v0.6.8

func EncodeHiddenRunes(s string) string

EncodeHiddenRunes percent-encodes every rune the terminal sanitizer would mask — C0/DEL, the 2-byte-encoded C1 range, bidi overrides and zero-width runes — so a value built from an untrusted source (a redirect-supplied final address, say) is recorded losslessly but can no longer smuggle terminal escapes or Trojan-Source reordering into a JSON surface or a committed baseline. This is the canonical encoder for the JSON/record boundary that Sanitize's doc note points at (iss-359); a value the terminal render path masks with '?' is encoded here instead, so the byte is preserved rather than substituted.

It is lossless in both directions a mask is not: a hidden rune is percent-encoded as its UTF-8 bytes, and a byte that is not valid UTF-8 — which strings.Map would silently rewrite to U+FFFD — is percent-encoded raw. A canonical address never carries these runes (net/url rejects C0 outright and percent-encodes the path itself), so encoding them cannot break a legitimate final URL.

func IsHidden added in v0.10.0

func IsHidden(r rune) bool

IsHidden reports whether r is a bidirectional control or a zero-width rune: a rune that makes rendered text read differently from its bytes, or hides text altogether. It is the predicate Sanitize masks these with, exported so a parser that refuses such runes outright judges them by the same set.

func OpensBalancedCodeSpan added in v0.7.1

func OpensBalancedCodeSpan(s string) bool

OpensBalancedCodeSpan reports whether s BEGINS with a backtick run that a later run of exactly the same length closes — that is, whether s starts with a code span rather than with literal backticks.

It exists so a caller that escapes leading block markers can ask the cleaner's own grammar instead of guessing. A leading run that opens a balanced span opens no block: a backtick fence's info string may not contain backticks, so a run with a matching closer on the same line is an inline span by construction, and escaping it would kill the shelter the cleaner's exemption relies on. Only an unbalanced leading run is a fence, and the cleaner no longer emits one.

func Sanitize

func Sanitize(s string) string

Sanitize replaces every terminal-display attack rune with a visible '?' (tab becomes a space). It neutralises:

  • C0 controls (<0x20) and DEL (0x7f) — these carry ESC, so a raw ANSI escape in a commit subject could recolour, move the cursor, or corrupt the report; a newline is masked too, so an injected line break cannot forge extra lines;
  • the C1 range (0x80–0x9F) — U+009B (CSI) acts like ESC[ on an 8-bit terminal, so masking ESC (a C0 control) alone would leave an equivalent path open;
  • bidirectional override/isolate controls (the "Trojan Source" class) and zero-width characters, which reorder or hide text so the rendered line differs from the bytes — the reader sees something the file does not say.

JSON output is NOT covered by encoding/json's own escaping: it escapes only C0 (below 0x20) and U+2028/9 — DEL, the C1 range, bidi overrides and zero-width runes pass through raw (pinned by TestJSONLeavesC1AndBidiRaw). A JSON surface whose strings reach a terminal or a committed record needs its values shape-checked or encoded at the boundary that produced them (iss-359); this function is the mask for the human/terminal render path.

func SanitizeAll

func SanitizeAll(in []string) []string

SanitizeAll sanitises every member of a slice, returning a new slice.

func SanitizeBlock

func SanitizeBlock(s string) string

SanitizeBlock sanitises multi-line text while keeping its line structure: each line is sanitised on its own and the line terminators are preserved. Sanitize masks a newline (an injected line break must not forge extra report lines), which is right for a value interpolated into one line and wrong for output that IS lines — a rendered diff or a quoted file excerpt, where flattening the terminators destroys the artefact the reader needs. Use this only where the line breaks are the render's own, never for a single untrusted value.

The carriage return of a CRLF pair survives; every other carriage return is masked. That distinction is load-bearing in both directions: a BARE CR moves the cursor to the column zero of the line being written, so it can overprint what the reader already saw — the attack Sanitize masks — whereas the CR of a CRLF is immediately committed by its newline and can overprint nothing. And a rendered patch against a CRLF file must carry those pairs or no patch tool will apply it, so dropping them would silently void the artefact.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL