-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathuser_agent_builder.go
More file actions
120 lines (96 loc) · 3.63 KB
/
user_agent_builder.go
File metadata and controls
120 lines (96 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package util
import (
"fmt"
"os"
"regexp"
"runtime"
"strings"
)
// Limit for MULTIAPPS_USER_AGENT_SUFFIX
const maxSuffixLength = 128
const DefaultCliVersion = "unknown-cf cli version"
// pluginVersion stores the version set from the main package
var pluginVersion string = "0.0.0"
// cfCliVersion stores the version set from the main package
var cfCliVersion string = DefaultCliVersion
// userAgentSuffixOption stores the default user agent suffix option set from the main package
var userAgentSuffixOption string = ""
// SetPluginVersion sets the plugin version for use in User-Agent
func SetPluginVersion(version string) {
pluginVersion = version
}
// SetCfCliVersion sets the cf CLI version for use in User-Agent
func SetCfCliVersion(version string) {
cfCliVersion = version
}
// SetUserAgentSuffixOption sets the default user agent suffix option for use in User-Agent
func SetUserAgentSuffixOption(suffix string) {
userAgentSuffixOption = suffix
}
// GetPluginVersion returns the current plugin version
func GetPluginVersion() string {
return pluginVersion
}
// GetCfCliVersion returns the current cf CLI version
func GetCfCliVersion() string {
return cfCliVersion
}
// GetUserAgentSuffixOption returns the current user agent suffix option
func GetUserAgentSuffixOption() string {
return userAgentSuffixOption
}
// BuildUserAgent creates a User-Agent string in the format:
// "Multiapps-CF-plugin/{version} ({operating system version}) {golang builder version} {custom_env_value}"
func BuildUserAgent() string {
osInformation := getOperatingSystemInformation()
goVersion := runtime.Version()
cfCliVersion := GetCfCliVersion()
customValue := getCustomEnvValue()
userAgent := fmt.Sprintf("Multiapps-CF-plugin/%s (%s) %s (%s)", pluginVersion, osInformation, goVersion, cfCliVersion)
if customValue != "" {
userAgent = fmt.Sprintf("%s %s", userAgent, customValue)
}
return userAgent
}
// getOperatingSystemInformation returns OS name and architecture
func getOperatingSystemInformation() string {
return fmt.Sprintf("%s %s", runtime.GOOS, runtime.GOARCH)
}
// getCustomEnvValue reads value from custom environment variable with validation
// Falls back to build-time value if environment variable is not set
func getCustomEnvValue() string {
value := os.Getenv("MULTIAPPS_USER_AGENT_SUFFIX")
if value == "" {
// Use build-time value as fallback
value = userAgentSuffixOption
}
if value == "" {
return ""
}
return sanitizeUserAgentSuffix(value)
}
// sanitizeUserAgentSuffix sanitizes the user agent suffix
func sanitizeUserAgentSuffix(value string) string {
// Security constraints for HTTP User-Agent header:
// 1. Max length to prevent server buffer overflow (Tomcat default: 8KB total headers)
// 2. Only allow safe characters to prevent header injection
// 3. Remove control characters and newlines
// Remove control characters, CR, LF, and other dangerous characters
value = strings.ReplaceAll(value, "\r", "")
value = strings.ReplaceAll(value, "\n", "")
value = strings.ReplaceAll(value, "\t", " ")
// Only allow ASCII characters, spaces, hyphens, dots, underscores, and alphanumeric
// This prevents header injection attacks
invalidChars := regexp.MustCompile(`[^a-zA-Z0-9 .\-_]`)
value = invalidChars.ReplaceAllString(value, "")
// Remove sequences that could be interpreted as header separators
value = strings.ReplaceAll(value, ":", "")
value = strings.ReplaceAll(value, ";", "")
// Trim whitespace and limit length
value = strings.TrimSpace(value)
if len(value) > maxSuffixLength {
value = value[:maxSuffixLength]
value = strings.TrimSpace(value) // Trim again in case we cut in the middle of whitespace
}
return value
}