chore: publish pxmon v0.2.0
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Privacy mode redacts sensitive tokens (IPs, long secret-like strings,
|
||||
// hostnames, MAC addresses, ssh key material) from rendered output. The
|
||||
// replacement uses a shifted block pattern (▚▞) which preserves token
|
||||
// length so the layout doesn't shift but makes the content obviously
|
||||
// unreadable — think "frosted glass" rather than the usual `****`.
|
||||
|
||||
var (
|
||||
privacyIPv4 = regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`)
|
||||
privacyIPv6 = regexp.MustCompile(`\b(?:[0-9a-fA-F]{1,4}:){2,}[0-9a-fA-F:]{0,}\b`)
|
||||
privacyMAC = regexp.MustCompile(`\b(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}\b`)
|
||||
privacyHost = regexp.MustCompile(`\b[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}(?:\.[a-zA-Z0-9\-]{1,63}){1,}\b`)
|
||||
privacyHex = regexp.MustCompile(`\b[A-Fa-f0-9]{24,}\b`)
|
||||
privacyB64 = regexp.MustCompile(`\b[A-Za-z0-9+/]{28,}={0,2}\b`)
|
||||
privacyKey = regexp.MustCompile(`(?i)(password|passwd|token|secret|apikey|api[_-]?key|bearer|authorization)\s*[:=]\s*\S+`)
|
||||
privacyUsrAt = regexp.MustCompile(`[A-Za-z0-9._-]+@[A-Za-z0-9.-]+`)
|
||||
)
|
||||
|
||||
// privacyGlyphs is a small set of dense unicode shade characters. By
|
||||
// cycling through them the redacted range looks like diffused noise
|
||||
// rather than a flat mask.
|
||||
var privacyGlyphs = []rune{'▚', '▞', '▓', '▒'}
|
||||
|
||||
// privacyMask returns a redaction string the same visual length as src.
|
||||
func privacyMask(src string) string {
|
||||
rs := []rune(src)
|
||||
out := make([]rune, len(rs))
|
||||
for i, r := range rs {
|
||||
if r == ' ' || r == '\t' || r == '\n' {
|
||||
out[i] = r
|
||||
continue
|
||||
}
|
||||
out[i] = privacyGlyphs[i%len(privacyGlyphs)]
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// privacyRedact scrubs every sensitive pattern from the given line. The
|
||||
// function is intentionally line-scoped — callers apply it row by row so
|
||||
// that multi-line ANSI layouts survive the substitution.
|
||||
func privacyRedact(line string) string {
|
||||
if line == "" {
|
||||
return line
|
||||
}
|
||||
replace := func(re *regexp.Regexp, s string) string {
|
||||
return re.ReplaceAllStringFunc(s, privacyMask)
|
||||
}
|
||||
// Order matters: scrub the longest/most specific patterns first so
|
||||
// later passes don't hit already-masked text.
|
||||
line = privacyKey.ReplaceAllStringFunc(line, func(m string) string {
|
||||
// Keep the label (password/token/etc) but mask the value.
|
||||
idx := strings.IndexAny(m, "=:")
|
||||
if idx < 0 {
|
||||
return privacyMask(m)
|
||||
}
|
||||
return m[:idx+1] + privacyMask(strings.TrimLeft(m[idx+1:], " "))
|
||||
})
|
||||
line = replace(privacyB64, line)
|
||||
line = replace(privacyHex, line)
|
||||
line = replace(privacyMAC, line)
|
||||
line = replace(privacyIPv4, line)
|
||||
line = replace(privacyIPv6, line)
|
||||
line = replace(privacyUsrAt, line)
|
||||
line = replace(privacyHost, line)
|
||||
return line
|
||||
}
|
||||
|
||||
// applyPrivacyMultiline redacts every line independently. Safe to call
|
||||
// on ANSI-styled output — masks only the literal runs a regex matches.
|
||||
func applyPrivacyMultiline(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
lines := strings.Split(s, "\n")
|
||||
for i, ln := range lines {
|
||||
lines[i] = privacyRedact(ln)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
Reference in New Issue
Block a user