Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 7 additions & 54 deletions common/log_fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package common
import (
"maps"
"slices"
"strings"

log "github.com/sirupsen/logrus"
)
Expand All @@ -46,70 +45,24 @@ var (
}
)

func isSensitiveLogKey(k string) bool {
key := strings.ToLower(k)
return strings.Contains(key, "passphrase") ||
strings.Contains(key, "password") ||
strings.Contains(key, "passwd") ||
strings.Contains(key, "token") ||
strings.Contains(key, "authorization") ||
strings.Contains(key, "cookie") ||
strings.Contains(key, "secret") ||
strings.Contains(key, "apikey") ||
strings.Contains(key, "api_key")
}

func sanitizeLogValue(k string, v interface{}) interface{} {
if isSensitiveLogKey(k) {
return "****"
}

switch ty := v.(type) {
case map[string]string:
redacted := map[string]string{}
for mk, mv := range ty {
if isSensitiveLogKey(mk) || k == "header" {
redacted[mk] = "****"
} else {
redacted[mk] = mv
}
}
return redacted
case map[string]interface{}:
redacted := map[string]interface{}{}
for mk, mv := range ty {
redacted[mk] = sanitizeLogValue(mk, mv)
}
return redacted
case []interface{}:
redacted := make([]interface{}, len(ty))
for i, iv := range ty {
redacted[i] = sanitizeLogValue(k, iv)
}
return redacted
default:
return ty
}
}

func FilterLogFields(src log.Fields, excludes ...string) log.Fields {
fields := log.Fields{}
for k, v := range src {
switch ty := v.(type) {
case map[string]string:
fields[k] = maps.Clone(ty)
case map[string]interface{}:
fields[k] = maps.Clone(ty)
case string:
if slices.Contains(nonEmptyFields, k) {
if len(ty) > 0 {
fields[k] = sanitizeLogValue(k, ty)
fields[k] = ty
}
} else {
fields[k] = sanitizeLogValue(k, ty)
fields[k] = ty
}
case map[string]string:
fields[k] = sanitizeLogValue(k, maps.Clone(ty))
case map[string]interface{}:
fields[k] = sanitizeLogValue(k, maps.Clone(ty))
default:
fields[k] = sanitizeLogValue(k, ty)
fields[k] = ty
}
Comment on lines 48 to 66
}

Expand Down
7 changes: 3 additions & 4 deletions http/webconfig_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@

for _, m := range messages {
if len(m.DeviceId) != 16 {
log.WithFields(tfields).Warn("invalid device_id")
log.WithFields(tfields).Warn("invalid device_id " + m.DeviceId)

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

Sensitive data returned by HTTP request headers
flows to a logging call.

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

Sensitive data returned by HTTP request headers
flows to a logging call.

Check warning

Code scanning / CodeQL

Log entries created from user input Medium

This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.

Check warning

Code scanning / CodeQL

Log entries created from user input Medium

This log entry depends on a
user-provided value
.
Comment thread
lstruman marked this conversation as resolved.
Dismissed
Comment thread
lstruman marked this conversation as resolved.
Dismissed
Comment thread
lstruman marked this conversation as resolved.
Dismissed
Comment thread
lstruman marked this conversation as resolved.
Dismissed
continue
Comment on lines 1065 to 1067
}
mac := m.DeviceId[4:]
Expand All @@ -1083,8 +1083,8 @@
}
s.Input() <- outMessage

tfields["output_key"] = "****"
tfields["output_body"] = "omitted"
tfields["output_key"] = mac
tfields["output_body"] = m
log.WithFields(tfields).Info("send")
Comment on lines +1086 to 1088
}
}
Expand All @@ -1093,7 +1093,6 @@
fields := xw.Audit()
fields["logger"] = "token"
tfields := common.FilterLogFields(fields)
delete(tfields, "header")
var headerMap map[string]string
var isObfuscated bool
if itf, ok := tfields["header"]; ok {
Comment on lines 1093 to 1098
Expand Down
24 changes: 0 additions & 24 deletions util/dict.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package util

import (
"net/http"
"strings"
"time"

log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -211,32 +210,9 @@ func (d Dict) Update(itf interface{}) {
}
}

func isSafeHeaderKey(key string) bool {
k := strings.ToLower(strings.TrimSpace(key))
switch k {
case "accept",
"accept-encoding",
"accept-language",
"cache-control",
"connection",
"content-length",
"content-type",
"host",
"pragma",
"user-agent":
return true
default:
return false
}
}

func HeaderToMap(header http.Header) map[string]string {
m := make(map[string]string)
for k, v := range header {
if !isSafeHeaderKey(k) {
m[k] = "****"
continue
}
if len(v) == 0 {
m[k] = ""
continue
Expand Down
14 changes: 13 additions & 1 deletion util/dict_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
*/
package util

import (
Expand Down Expand Up @@ -144,3 +144,15 @@ func TestHeaderToMap(t *testing.T) {
m := HeaderToMap(header)
assert.DeepEqual(t, m, expected)
}

func TestHeaderToMapEmptyValueSlice(t *testing.T) {
// A header key present with no values should not panic and should map to "".
header := http.Header{
"Content-Type": {"application/json"},
"X-Empty": {},
}

m := HeaderToMap(header)
assert.Equal(t, m["Content-Type"], "application/json")
assert.Equal(t, m["X-Empty"], "")
}
Loading