[mirror] Scalable static site server for Git forges (like GitHub Pages)

Scrub the Forge-Authorization header from Sentry events.

miyuko c5df1166 71fd1c39

Changed files
+66 -28
src
+66 -28
src/observe.go
··· 51 51 return os.Getenv("SENTRY_DSN") != "" 52 52 } 53 53 54 + func chainSentryMiddleware( 55 + middleware ...func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event, 56 + ) func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { 57 + return func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { 58 + for idx := 0; idx < len(middleware) && event != nil; idx++ { 59 + event = middleware[idx](event, hint) 60 + } 61 + return event 62 + } 63 + } 64 + 65 + // sensitiveHTTPHeaders extends the list of sensitive headers defined in the Sentry Go SDK with our 66 + // own application-specific header field names. 67 + var sensitiveHTTPHeaders = map[string]struct{}{ 68 + "Forge-Authorization": {}, 69 + } 70 + 71 + // scrubSentryEvent removes sensitive HTTP header fields from the Sentry event. 72 + func scrubSentryEvent(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { 73 + if event.Request != nil && event.Request.Headers != nil { 74 + for key := range event.Request.Headers { 75 + if _, ok := sensitiveHTTPHeaders[key]; ok { 76 + delete(event.Request.Headers, key) 77 + } 78 + } 79 + } 80 + return event 81 + } 82 + 83 + // sampleSentryEvent returns a function that discards a Sentry event according to the sample rate, 84 + // unless the associated HTTP request triggers a mutation or it took too long to produce a response, 85 + // in which case the event is never discarded. 86 + func sampleSentryEvent(sampleRate float64) func(*sentry.Event, *sentry.EventHint) *sentry.Event { 87 + return func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { 88 + newSampleRate := sampleRate 89 + if event.Request != nil { 90 + switch event.Request.Method { 91 + case "PUT", "POST", "DELETE": 92 + newSampleRate = 1 93 + } 94 + } 95 + duration := event.Timestamp.Sub(event.StartTime) 96 + threshold := time.Duration(config.Observability.SlowResponseThreshold) 97 + if duration >= threshold { 98 + newSampleRate = 1 99 + } 100 + if rand.Float64() < newSampleRate { 101 + return event 102 + } 103 + return nil 104 + } 105 + } 106 + 54 107 func InitObservability() { 55 108 debug.SetPanicOnFault(true) 56 109 ··· 98 151 enableTracing = value 99 152 } 100 153 154 + tracesSampleRate := 1.00 155 + switch environment { 156 + case "development", "staging": 157 + default: 158 + tracesSampleRate = 0.05 159 + } 160 + 101 161 options := sentry.ClientOptions{} 102 162 options.DisableTelemetryBuffer = !config.Feature("sentry-telemetry-buffer") 103 163 options.Environment = environment 104 164 options.EnableLogs = enableLogs 105 165 options.EnableTracing = enableTracing 106 - options.TracesSampleRate = 1 107 - switch environment { 108 - case "development", "staging": 109 - default: 110 - options.BeforeSendTransaction = func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { 111 - sampleRate := 0.05 112 - if trace, ok := event.Contexts["trace"]; ok { 113 - if data, ok := trace["data"].(map[string]any); ok { 114 - if method, ok := data["http.request.method"].(string); ok { 115 - switch method { 116 - case "PUT", "DELETE", "POST": 117 - sampleRate = 1 118 - default: 119 - duration := event.Timestamp.Sub(event.StartTime) 120 - threshold := time.Duration(config.Observability.SlowResponseThreshold) 121 - if duration >= threshold { 122 - sampleRate = 1 123 - } 124 - } 125 - } 126 - } 127 - } 128 - if rand.Float64() < sampleRate { 129 - return event 130 - } 131 - return nil 132 - } 133 - } 166 + options.TracesSampleRate = 1 // use our own custom sampling logic 167 + options.BeforeSend = scrubSentryEvent 168 + options.BeforeSendTransaction = chainSentryMiddleware( 169 + sampleSentryEvent(tracesSampleRate), 170 + scrubSentryEvent, 171 + ) 134 172 if err := sentry.Init(options); err != nil { 135 173 log.Fatalf("sentry: %s\n", err) 136 174 }