|
1 | 1 | package analytics
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "encoding/json" |
4 | 5 | "errors"
|
5 | 6 | "fmt"
|
| 7 | + "os/user" |
6 | 8 | "time"
|
7 | 9 |
|
| 10 | + "github.com/rs/zerolog" |
| 11 | + |
8 | 12 | "github.com/snyk/error-catalog-golang-public/snyk_errors"
|
9 | 13 |
|
10 | 14 | api "github.com/snyk/go-application-framework/internal/api/clients"
|
@@ -62,6 +66,18 @@ type instrumentationCollectorImpl struct {
|
62 | 66 | extension map[string]interface{}
|
63 | 67 | }
|
64 | 68 |
|
| 69 | +type serializeOptions struct { |
| 70 | + logger *zerolog.Logger |
| 71 | +} |
| 72 | + |
| 73 | +type serializeOptionFunc func(*serializeOptions) |
| 74 | + |
| 75 | +func WithLogger(logger *zerolog.Logger) serializeOptionFunc { |
| 76 | + return func(o *serializeOptions) { |
| 77 | + o.logger = logger |
| 78 | + } |
| 79 | +} |
| 80 | + |
65 | 81 | func (ic *instrumentationCollectorImpl) SetUserAgent(ua networking.UserAgentInfo) {
|
66 | 82 | ic.userAgent = ua
|
67 | 83 | }
|
@@ -121,25 +137,75 @@ func (ic *instrumentationCollectorImpl) AddExtension(key string, value interface
|
121 | 137 | ic.extension[key] = value
|
122 | 138 | }
|
123 | 139 |
|
124 |
| -func GetV2InstrumentationObject(collector InstrumentationCollector) (*api.AnalyticsRequestBody, error) { |
| 140 | +func GetV2InstrumentationObject(collector InstrumentationCollector, opt ...serializeOptionFunc) (*api.AnalyticsRequestBody, error) { |
125 | 141 | t, ok := collector.(*instrumentationCollectorImpl)
|
126 |
| - if ok { |
127 |
| - return t.GetV2InstrumentationObject(), nil |
| 142 | + if !ok { |
| 143 | + return nil, fmt.Errorf("failed to convert collector") |
| 144 | + } |
| 145 | + |
| 146 | + logger := zerolog.Nop() |
| 147 | + options := serializeOptions{ |
| 148 | + logger: &logger, |
128 | 149 | }
|
129 |
| - return nil, fmt.Errorf("failed to convert collector") |
| 150 | + for _, o := range opt { |
| 151 | + o(&options) |
| 152 | + } |
| 153 | + |
| 154 | + return t.getV2InstrumentationObject(options.logger), nil |
130 | 155 | }
|
131 | 156 |
|
132 |
| -func (ic *instrumentationCollectorImpl) GetV2InstrumentationObject() *api.AnalyticsRequestBody { |
| 157 | +func (ic *instrumentationCollectorImpl) getV2InstrumentationObject(logger *zerolog.Logger) *api.AnalyticsRequestBody { |
133 | 158 | a := ic.getV2Attributes()
|
134 | 159 |
|
135 | 160 | d := api.AnalyticsData{
|
136 | 161 | Type: ic.instrumentationType,
|
137 | 162 | Attributes: a,
|
138 | 163 | }
|
139 | 164 |
|
140 |
| - return &api.AnalyticsRequestBody{ |
| 165 | + return ic.sanitizeExtensionData(logger, d) |
| 166 | +} |
| 167 | + |
| 168 | +// Since the `extension` attribute in the analytics payload is a value any |
| 169 | +// product line potentially can contribute to, we utilize the same sanitation logic |
| 170 | +// already in place for the legacy v1 analytics, to ensure the same level of PII protection. |
| 171 | +func (ic *instrumentationCollectorImpl) sanitizeExtensionData(logger *zerolog.Logger, d api.AnalyticsData) *api.AnalyticsRequestBody { |
| 172 | + extension, err := json.Marshal(d.Attributes.Interaction.Extension) |
| 173 | + result := &api.AnalyticsRequestBody{ |
141 | 174 | Data: d,
|
142 | 175 | }
|
| 176 | + result.Data.Attributes.Interaction.Extension = nil |
| 177 | + |
| 178 | + if err != nil { |
| 179 | + logger.Printf("failed to marshal extension, removing extension object from analytics payload: %v", err) |
| 180 | + return result |
| 181 | + } |
| 182 | + |
| 183 | + var sanitized []byte |
| 184 | + sanitized, err = SanitizeValuesByKey(sensitiveFieldNames, sanitizeReplacementString, extension) |
| 185 | + if err != nil { |
| 186 | + logger.Printf("failed to sanitize extension, removing object from analytics payload as sanitzation was not possible: %v", err) |
| 187 | + return result |
| 188 | + } |
| 189 | + |
| 190 | + u, err := user.Current() |
| 191 | + if err != nil { |
| 192 | + logger.Printf("failed to find user information while sanitizing extension payload, removing object from analytics payload as sanitzation was not possible: %v", err) |
| 193 | + return result |
| 194 | + } |
| 195 | + |
| 196 | + sanitized, err = SanitizeUsername(u.Username, u.HomeDir, sanitizeReplacementString, sanitized) |
| 197 | + if err != nil { |
| 198 | + logger.Printf("failed to sanitize user information in extension payload, removing object from analytics payload as sanitzation was not possible: %v", err) |
| 199 | + return result |
| 200 | + } |
| 201 | + |
| 202 | + err = json.Unmarshal(sanitized, &result.Data.Attributes.Interaction.Extension) |
| 203 | + if err != nil { |
| 204 | + logger.Printf("failed to unmarshal sanitized extension object:: %v", err) |
| 205 | + return result |
| 206 | + } |
| 207 | + |
| 208 | + return result |
143 | 209 | }
|
144 | 210 |
|
145 | 211 | func (ic *instrumentationCollectorImpl) getV2Attributes() api.AnalyticsAttributes {
|
|
0 commit comments