@@ -18,25 +18,6 @@ import (
1818 "github.com/miekg/dns"
1919)
2020
21- // sanitizeString - Sanitizes user input to prevent XSS attacks
22- func sanitizeString (input string ) string {
23- // HTML escape to prevent XSS
24- escaped := html .EscapeString (input )
25- // Additional validation for domain names - only allow valid domain characters
26- if strings .Contains (input , "." ) { // Likely a domain name
27- // Remove any non-domain characters
28- var result strings.Builder
29- for _ , r := range escaped {
30- if (r >= 'a' && r <= 'z' ) || (r >= 'A' && r <= 'Z' ) ||
31- (r >= '0' && r <= '9' ) || r == '.' || r == '-' || r == '_' {
32- result .WriteRune (r )
33- }
34- }
35- return result .String ()
36- }
37- return escaped
38- }
39-
4021// MonitoringUIConfig - Configuration for the monitoring UI
4122type MonitoringUIConfig struct {
4223 Enabled bool `toml:"enabled"`
@@ -360,10 +341,11 @@ func (ui *MonitoringUI) UpdateMetrics(pluginsState PluginsState, msg *dns.Msg, s
360341
361342 // Update top domains - separate lock
362343 if mc .privacyLevel < 2 {
363- sanitizedDomain := sanitizeString (pluginsState .qName )
344+ // Store domain name directly - no sanitization needed for internal metrics
345+ domainName := pluginsState .qName
364346 mc .domainMutex .Lock ()
365- mc .topDomains [sanitizedDomain ]++
366- dlog .Debugf ("Domain %s, count: %d" , sanitizedDomain , mc .topDomains [sanitizedDomain ])
347+ mc .topDomains [domainName ]++
348+ dlog .Debugf ("Domain %s, count: %d" , domainName , mc .topDomains [domainName ])
367349 mc .domainMutex .Unlock ()
368350 }
369351
@@ -410,13 +392,14 @@ func (ui *MonitoringUI) UpdateMetrics(pluginsState PluginsState, msg *dns.Msg, s
410392 }
411393
412394 entry := QueryLogEntry {
413- Timestamp : now ,
414- ClientIP : clientIP ,
415- Domain : sanitizeString (pluginsState .qName ),
416- Type : sanitizeString (qType ),
417- ResponseCode : sanitizeString (returnCode ),
395+ Timestamp : now ,
396+ ClientIP : clientIP ,
397+ // HTML escape only the fields that will be displayed in web UI
398+ Domain : html .EscapeString (pluginsState .qName ),
399+ Type : qType , // DNS types are safe, no escaping needed
400+ ResponseCode : returnCode , // DNS response codes are safe, no escaping needed
418401 ResponseTime : responseTime ,
419- Server : sanitizeString (pluginsState .serverName ),
402+ Server : html . EscapeString (pluginsState .serverName ),
420403 CacheHit : pluginsState .cacheHit ,
421404 }
422405
@@ -527,17 +510,19 @@ func (mc *MetricsCollector) generatePrometheusMetrics() string {
527510 result .WriteString ("# HELP dnscrypt_proxy_server_queries_total Total queries per server\n " )
528511 result .WriteString ("# TYPE dnscrypt_proxy_server_queries_total counter\n " )
529512 for server , count := range mc .serverQueryCount {
530- sanitizedServer := sanitizeString (server )
531- result .WriteString (fmt .Sprintf ("dnscrypt_proxy_server_queries_total{server=\" %s\" } %d\n " , sanitizedServer , count ))
513+ // For Prometheus labels, escape quotes and backslashes to prevent label injection
514+ escapedServer := strings .ReplaceAll (strings .ReplaceAll (server , "\\ " , "\\ \\ " ), "\" " , "\\ \" " )
515+ result .WriteString (fmt .Sprintf ("dnscrypt_proxy_server_queries_total{server=\" %s\" } %d\n " , escapedServer , count ))
532516 }
533517
534518 result .WriteString ("# HELP dnscrypt_proxy_server_response_time_average_ms Average response time per server in milliseconds\n " )
535519 result .WriteString ("# TYPE dnscrypt_proxy_server_response_time_average_ms gauge\n " )
536520 for server , count := range mc .serverQueryCount {
537521 if count > 0 {
538522 avgTime := float64 (mc .serverResponseTime [server ]) / float64 (count )
539- sanitizedServer := sanitizeString (server )
540- result .WriteString (fmt .Sprintf ("dnscrypt_proxy_server_response_time_average_ms{server=\" %s\" } %.2f\n " , sanitizedServer , avgTime ))
523+ // For Prometheus labels, escape quotes and backslashes to prevent label injection
524+ escapedServer := strings .ReplaceAll (strings .ReplaceAll (server , "\\ " , "\\ \\ " ), "\" " , "\\ \" " )
525+ result .WriteString (fmt .Sprintf ("dnscrypt_proxy_server_response_time_average_ms{server=\" %s\" } %.2f\n " , escapedServer , avgTime ))
541526 }
542527 }
543528 mc .serverMutex .RUnlock ()
@@ -547,8 +532,8 @@ func (mc *MetricsCollector) generatePrometheusMetrics() string {
547532 result .WriteString ("# HELP dnscrypt_proxy_query_type_total Total queries per DNS record type\n " )
548533 result .WriteString ("# TYPE dnscrypt_proxy_query_type_total counter\n " )
549534 for qtype , count := range mc .queryTypes {
550- sanitizedQtype := sanitizeString ( qtype )
551- result .WriteString (fmt .Sprintf ("dnscrypt_proxy_query_type_total{type=\" %s\" } %d\n " , sanitizedQtype , count ))
535+ // DNS query types are safe alphanumeric values, no escaping needed
536+ result .WriteString (fmt .Sprintf ("dnscrypt_proxy_query_type_total{type=\" %s\" } %d\n " , qtype , count ))
552537 }
553538 mc .queryTypesMutex .RUnlock ()
554539
@@ -688,7 +673,7 @@ func (mc *MetricsCollector) GetMetrics() map[string]interface{} {
688673 count := 0
689674 for _ , dc := range domainCounts {
690675 topDomainsList = append (topDomainsList , map [string ]interface {}{
691- "domain" : sanitizeString (dc .domain ),
676+ "domain" : html . EscapeString (dc .domain ),
692677 "count" : dc .count ,
693678 })
694679 count ++
0 commit comments