Replaces #559, which conflicted. Model: opus-5-5
This commit is contained in:
@@ -13,12 +13,12 @@ import (
|
||||
const RedactionMarker = "(redacted)"
|
||||
|
||||
// Redactor removes one target's own credential material from
|
||||
// text that target's remote peer chose: a delivery response
|
||||
// body, or a delivery error stored before the delivery path
|
||||
// learned to mask the URLs it embeds.
|
||||
// a delivery response body, whose text the target's remote
|
||||
// peer chose, and from a stored delivery error as a second
|
||||
// line behind the delivery path's own URL masking.
|
||||
//
|
||||
// It removes byte-identical echoes of strings taken from the
|
||||
// target's stored configuration, and nothing else. Anything
|
||||
// target's URL and headers, and nothing else. Anything
|
||||
// the remote re-encodes survives: JSON "\/" escaping (what
|
||||
// PHP's json_encode emits by default), percent-encoding, HTML
|
||||
// entities, and an echo of only part of a path. It cannot
|
||||
@@ -115,7 +115,7 @@ func (r Redactor) secretPrefixSuffix(s string) int {
|
||||
}
|
||||
|
||||
// targetSecrets returns the credential-bearing strings a
|
||||
// target's configuration carries.
|
||||
// target's settings carry.
|
||||
//
|
||||
// The destination URL contributes. Its path, query and
|
||||
// userinfo are the credential for both target types that have
|
||||
@@ -129,51 +129,34 @@ func (r Redactor) secretPrefixSuffix(s string) int {
|
||||
// picks out. That is the same class-based rule applied to
|
||||
// URLs: an echoed Accept or User-Agent still renders, an
|
||||
// echoed Authorization does not.
|
||||
//
|
||||
// A setting a type does not use is empty and contributes
|
||||
// nothing, so a database or log target has nothing to redact.
|
||||
func targetSecrets(t *database.Target) []string {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch t.Type {
|
||||
case database.TargetTypeSlack:
|
||||
cfg, err := parseSlackConfig(t.Config)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return urlSecrets(cfg.WebhookURL)
|
||||
case database.TargetTypeHTTP:
|
||||
cfg, err := parseHTTPConfig(t.Config)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return append(
|
||||
urlSecrets(cfg.URL),
|
||||
headerSecrets(cfg.Headers)...,
|
||||
)
|
||||
case database.TargetTypeDatabase, database.TargetTypeLog:
|
||||
// Neither has a destination URL, so neither has
|
||||
// anything to redact.
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return append(urlSecrets(t.URL), headerSecrets(t.Headers)...)
|
||||
}
|
||||
|
||||
// urlSecrets returns the substrings of a destination URL that
|
||||
// must not survive into a rendered page: the whole URL, the
|
||||
// parts of it MaskURL elides, and any userinfo.
|
||||
// must not survive into a rendered page: the whole URL; its
|
||||
// path, unless that is empty or "/"; its query string, and the
|
||||
// request URI that carries it, which a remote echoing the
|
||||
// request line shows even when the URL has no path; and its
|
||||
// userinfo and password.
|
||||
//
|
||||
// No length floor is applied to the path, and none to the
|
||||
// userinfo. A short path or a four-byte username is treated as
|
||||
// a credential exactly like a long one, because the field takes
|
||||
// an arbitrary URL and no part of it can be assumed non-secret —
|
||||
// the same rule MaskURL applies. headerSecrets does carry a
|
||||
// floor, and the difference is deliberate: a header is picked
|
||||
// out by a name-shaped guess and its value may be ordinary
|
||||
// text, whereas a URL's path and userinfo are credential
|
||||
// material by position.
|
||||
// No length floor is applied to the path, the query string or
|
||||
// the userinfo. A short path or a four-byte username is
|
||||
// treated as a credential exactly like a long one, because the
|
||||
// field takes an arbitrary URL and no part of it can be
|
||||
// assumed non-secret — the same rule MaskURL applies.
|
||||
// headerSecrets does carry a floor, and the difference is
|
||||
// deliberate: a header is picked out by a name-shaped guess
|
||||
// and its value may be ordinary text, whereas a URL's path,
|
||||
// query string and userinfo are credential material by
|
||||
// position.
|
||||
func urlSecrets(raw string) []string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
@@ -188,12 +171,11 @@ func urlSecrets(raw string) []string {
|
||||
}
|
||||
|
||||
if parsed.Path != "" && parsed.Path != "/" {
|
||||
requestURI := parsed.RequestURI()
|
||||
secrets = append(secrets, requestURI)
|
||||
secrets = append(secrets, parsed.EscapedPath())
|
||||
}
|
||||
|
||||
if escaped := parsed.EscapedPath(); escaped != requestURI {
|
||||
secrets = append(secrets, escaped)
|
||||
}
|
||||
if parsed.RawQuery != "" {
|
||||
secrets = append(secrets, parsed.RequestURI(), parsed.RawQuery)
|
||||
}
|
||||
|
||||
if parsed.User != nil {
|
||||
|
||||
Reference in New Issue
Block a user