Skip to content

Commit

Permalink
authres: Properly quote property values with special parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
foxcpp authored and emersion committed Oct 21, 2019
1 parent dc24987 commit 74b0c47
Showing 1 changed file with 65 additions and 1 deletion.
66 changes: 65 additions & 1 deletion authres/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package authres

import (
"sort"
"strings"
"unicode"
)

// Format formats an Authentication-Results header.
Expand Down Expand Up @@ -69,9 +71,71 @@ func formatParams(params map[string]string) string {
if i > 0 {
s += " "
}
s += k + "=" + params[k]

var value string
if k == "reason" {
value = formatValue(params[k])
} else {
value = formatPvalue(params[k])
}
s += k + "=" + value
i++
}

return s
}

var tspecials = map[rune]struct{}{
'(': {}, ')': {}, '<': {}, '>': {}, '@': {},
',': {}, ';': {}, ':': {}, '\\': {}, '"': {},
'/': {}, '[': {}, ']': {}, '?': {}, '=': {},
}

func formatValue(s string) string {
// value := token / quoted-string
// token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
// or tspecials>
// tspecials := "(" / ")" / "<" / ">" / "@" /
// "," / ";" / ":" / "\" / <">
// "/" / "[" / "]" / "?" / "="
// ; Must be in quoted-string,
// ; to use within parameter values

shouldQuote := false
for _, ch := range s {
if _, special := tspecials[ch]; ch <= ' ' /* SPACE or CTL */ || special {
shouldQuote = true
}
}

if shouldQuote {
// None of involved specs specify how to handle " in quoted strings
// so we just drop them and hope they are not criticial.
return `"` + strings.Replace(s, `"`, ``, -1) + `"`
}
return s
}

func formatPvalue(s string) string {
// pvalue = [CFWS] ( value / [ [ local-part ] "@" ] domain-name )
// [CFWS]

// Experience shows that implementers often "forget" that things can
// be quoted in various places where they are usually not quoted
// so we can't get away by just quoting everything.

// Relevant ABNF rules are much complicated than that, but this
// will catch most of the cases and we can fallback to quoting
// for others.
addressLike := true
for _, ch := range s {
if !unicode.IsLetter(ch) && !unicode.IsDigit(ch) || ch != '@' {
addressLike = false
}
}

if addressLike {
return s
}
return formatValue(s)
}

0 comments on commit 74b0c47

Please sign in to comment.