- remove nil-check for context; passing a nil-context is never ok
- handle non-string values
- optimize escapeStr; headers should always be ASCII, so no need to
loop over runes. Also ignore newlines and control-chars.
Signed-off-by: Ritesh Vishwakarma <riteshvishwakarma.work@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -2,7 +2,6 @@ package dockerversion |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"context" |
| 5 |
- "fmt" |
|
| 6 | 5 |
"runtime" |
| 7 | 6 |
"strings" |
| 8 | 7 |
"sync" |
| ... | ... |
@@ -34,7 +33,7 @@ var ( |
| 34 | 34 |
// getDaemonUserAgent returns the user-agent to use for requests made by |
| 35 | 35 |
// the daemon. |
| 36 | 36 |
// |
| 37 |
-// It includes; |
|
| 37 |
+// It includes: |
|
| 38 | 38 |
// |
| 39 | 39 |
// - the docker version |
| 40 | 40 |
// - go version |
| ... | ... |
@@ -65,35 +64,32 @@ func getDaemonUserAgent() string {
|
| 65 | 65 |
// |
| 66 | 66 |
// It returns an empty string if no user-agent is present in the context. |
| 67 | 67 |
func getUpstreamUserAgent(ctx context.Context) string {
|
| 68 |
- var upstreamUA string |
|
| 69 |
- if ctx != nil {
|
|
| 70 |
- if ki := ctx.Value(UAStringKey{}); ki != nil {
|
|
| 71 |
- upstreamUA = ctx.Value(UAStringKey{}).(string)
|
|
| 72 |
- } |
|
| 73 |
- } |
|
| 74 |
- if upstreamUA == "" {
|
|
| 68 |
+ upstreamUA, ok := ctx.Value(UAStringKey{}).(string)
|
|
| 69 |
+ if !ok || upstreamUA == "" {
|
|
| 75 | 70 |
return "" |
| 76 | 71 |
} |
| 77 |
- return fmt.Sprintf("UpstreamClient(%s)", escapeStr(upstreamUA))
|
|
| 78 |
-} |
|
| 79 | 72 |
|
| 80 |
-const charsToEscape = `();\` |
|
| 73 |
+ return "UpstreamClient(" + escapeStr(upstreamUA) + ")"
|
|
| 74 |
+} |
|
| 81 | 75 |
|
| 82 |
-// escapeStr returns s with every rune in charsToEscape escaped by a backslash |
|
| 76 |
+// escapeStr escapes and sanitizes s for use in a User-Agent comment. |
|
| 83 | 77 |
func escapeStr(s string) string {
|
| 84 |
- var ret strings.Builder |
|
| 85 |
- for _, currRune := range s {
|
|
| 86 |
- appended := false |
|
| 87 |
- for _, escapableRune := range charsToEscape {
|
|
| 88 |
- if currRune == escapableRune {
|
|
| 89 |
- ret.WriteString(`\` + string(currRune)) |
|
| 90 |
- appended = true |
|
| 91 |
- break |
|
| 78 |
+ var b strings.Builder |
|
| 79 |
+ b.Grow(len(s)) |
|
| 80 |
+ |
|
| 81 |
+ for i := range len(s) {
|
|
| 82 |
+ switch c := s[i]; c {
|
|
| 83 |
+ case '(', ')', ';', '\\':
|
|
| 84 |
+ b.WriteByte('\\')
|
|
| 85 |
+ b.WriteByte(c) |
|
| 86 |
+ case '\t': |
|
| 87 |
+ b.WriteByte(c) |
|
| 88 |
+ default: |
|
| 89 |
+ if c >= 0x20 && c != 0x7f {
|
|
| 90 |
+ b.WriteByte(c) |
|
| 92 | 91 |
} |
| 93 | 92 |
} |
| 94 |
- if !appended {
|
|
| 95 |
- ret.WriteRune(currRune) |
|
| 96 |
- } |
|
| 97 | 93 |
} |
| 98 |
- return ret.String() |
|
| 94 |
+ |
|
| 95 |
+ return b.String() |
|
| 99 | 96 |
} |
| ... | ... |
@@ -49,6 +49,11 @@ func TestDockerUserAgent(t *testing.T) {
|
| 49 | 49 |
ctx: context.WithValue(t.Context(), UAStringKey{}, `Magic-Client/1.2.3 (linux); \ test`),
|
| 50 | 50 |
expected: getDaemonUserAgent() + ` UpstreamClient(Magic-Client/1.2.3 \(linux\)\; \\ test)`, |
| 51 | 51 |
}, |
| 52 |
+ {
|
|
| 53 |
+ doc: "daemon user-agent with upstream control chars", |
|
| 54 |
+ ctx: context.WithValue(t.Context(), UAStringKey{}, "Magic-Client/1.2.3\r\nInjected: evil"),
|
|
| 55 |
+ expected: getDaemonUserAgent() + ` UpstreamClient(Magic-Client/1.2.3Injected: evil)`, |
|
| 56 |
+ }, |
|
| 52 | 57 |
} |
| 53 | 58 |
|
| 54 | 59 |
for _, tc := range tests {
|