pi-agent: Implement final TUI hardening
This commit is contained in:
@@ -263,19 +263,26 @@ func normalizeBypassEntry(raw string) (string, error) {
|
||||
return host, nil
|
||||
}
|
||||
|
||||
var credentialPattern = regexp.MustCompile(`(?i)((?:https?|socks5h?)://)?([^/@\s:]+):([^@\s]*)@`)
|
||||
var (
|
||||
schemeCredentialPattern = regexp.MustCompile(`(?i)((?:https?|socks5h?)://)([^/@:\r\n]+):([^@\r\n]*)@`)
|
||||
bareCredentialPattern = regexp.MustCompile(`(?i)([^/@\s:]+):([^@/\s]*)@`)
|
||||
)
|
||||
|
||||
// RedactText removes URL passwords from diagnostic text without requiring the
|
||||
// surrounding text to be a valid URL or to include a scheme.
|
||||
// surrounding text to be a valid URL. The scheme-aware first pass deliberately
|
||||
// accepts spaces and other malformed password bytes up to @; diagnostics and
|
||||
// form validation must not expose a credential merely because parsing failed.
|
||||
func RedactText(value string) string {
|
||||
return credentialPattern.ReplaceAllString(value, `${1}${2}:REDACTED@`)
|
||||
value = schemeCredentialPattern.ReplaceAllString(value, `${1}${2}:REDACTED@`)
|
||||
return bareCredentialPattern.ReplaceAllString(value, `${1}:REDACTED@`)
|
||||
}
|
||||
|
||||
// RedactUserinfoText removes usernames as well as passwords. Form frames use
|
||||
// this stricter boundary because even the identity of the probe source is
|
||||
// private while a Profile is being edited.
|
||||
func RedactUserinfoText(value string) string {
|
||||
return credentialPattern.ReplaceAllString(value, `${1}REDACTED@`)
|
||||
value = schemeCredentialPattern.ReplaceAllString(value, `${1}REDACTED@`)
|
||||
return bareCredentialPattern.ReplaceAllString(value, `REDACTED@`)
|
||||
}
|
||||
|
||||
func ObscureURL(value string) string {
|
||||
@@ -294,14 +301,19 @@ func ObscureURL(value string) string {
|
||||
// rune count. A text input clone can therefore retain its real cursor and
|
||||
// horizontal viewport positions without rendering either username or password.
|
||||
func ObscureURLForEditing(value string) string {
|
||||
matches := credentialPattern.FindAllStringSubmatchIndex(value, -1)
|
||||
masked := maskCredentialMatches(value, schemeCredentialPattern, 4, 7)
|
||||
return maskCredentialMatches(masked, bareCredentialPattern, 2, 5)
|
||||
}
|
||||
|
||||
func maskCredentialMatches(value string, pattern *regexp.Regexp, userinfoStartGroup, userinfoEndGroup int) string {
|
||||
matches := pattern.FindAllStringSubmatchIndex(value, -1)
|
||||
if len(matches) == 0 {
|
||||
return value
|
||||
}
|
||||
var masked strings.Builder
|
||||
last := 0
|
||||
for _, match := range matches {
|
||||
userinfoStart, userinfoEnd := match[4], match[7]
|
||||
userinfoStart, userinfoEnd := match[userinfoStartGroup], match[userinfoEndGroup]
|
||||
masked.WriteString(value[last:userinfoStart])
|
||||
masked.WriteString(strings.Repeat("•", len([]rune(value[userinfoStart:userinfoEnd]))))
|
||||
last = userinfoEnd
|
||||
|
||||
Reference in New Issue
Block a user