Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(go): add log output neutralization (CWE-117) #275

Merged
merged 1 commit into from
Feb 19, 2024
Merged
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
151 changes: 151 additions & 0 deletions rules/go/lang/log_output_neutralization.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
imports:
- go_shared_lang_dynamic_request_input
patterns:
- pattern: |
$<CALLER>.$<METHOD>($<INPUT>);
filters:
- either:
- variable: CALLER
detection: go_lang_log_output_neutralization_logger
- variable: CALLER
detection: go_lang_log_output_neutralization_zerolog
- variable: METHOD
regex: \A(Fatal|Panic|Print)(f|ln)?\z
- variable: INPUT
detection: go_lang_log_output_neutralization_input
- pattern: |
$<ZEROLOG>.$<EVENT>.$<METHOD>($<INPUT>);
filters:
- variable: ZEROLOG
detection: go_lang_log_output_neutralization_zerolog
- variable: EVENT
regex: \A(Info|Debug|Error|Trace|Fatal|Panic|Warn)\z
- variable: METHOD
values:
- Msg
- Msgf
- variable: INPUT
detection: go_lang_log_output_neutralization_input
- pattern: |
$<CALLER>.$<METHOD>($<INPUT>);
filters:
- either:
- variable: CALLER
detection: go_lang_log_output_neutralization_logrus
- variable: CALLER
detection: go_lang_log_output_neutralization_seelog
- variable: METHOD
regex: \A(WithFields\.)?(Info|Debug|Error|Trace|Fatal|Panic|Warn)\z
- variable: INPUT
detection: go_lang_log_output_neutralization_input
- pattern: |
$<CALLER>.$<METHOD>($<INPUT>);
filters:
- variable: CALLER
detection: go_lang_log_output_neutralization_glog
- variable: METHOD
regex: \A(Info|Warning|Error|Fatal)(Contex)?(Depth)?(f)?\z
- variable: INPUT
detection: go_lang_log_output_neutralization_input
- pattern: |
$<ZAP>.$<METHOD>($<INPUT>);
filters:
- variable: ZAP
detection: go_lang_log_output_neutralization_zap
- variable: METHOD
regex: \A(WithFields\.)?(Info|Log|Error|Fatal|DPanic|Warn)\z
- variable: INPUT
detection: go_lang_log_output_neutralization_input
auxiliary:
- id: go_lang_log_output_neutralization_logger
cfabianski marked this conversation as resolved.
Show resolved Hide resolved
patterns:
- log.New();
- log.Default();
- id: go_lang_log_output_neutralization_zerolog
patterns:
- import $<!>"github.com/rs/zerolog"
- |
import (
$<!>"github.com/rs/zerolog"
)
- id: go_lang_log_output_neutralization_logrus
patterns:
- logrus.New();
- import $<!>"github.com/sirupsen/logrus"
- |
import (
$<!>"github.com/sirupsen/logrus"
)
- id: go_lang_log_output_neutralization_zap
patterns:
- zap.$<_>().Sugar()
- zap.$<_>()
- id: go_lang_log_output_neutralization_seelog
patterns:
- import $<!>"github.com/cihub/seelog"
- |
import (
$<!>"github.com/cihub/seelog"
)
- id: go_lang_log_output_neutralization_glog
patterns:
- import $<!>"github.com/golang/glog"
- |
import (
$<!>"github.com/golang/glog"
)
- id: go_lang_log_output_neutralization_input
sanitizer: go_lang_log_output_neutralization_sanitizer
patterns:
- pattern: $<INPUT>
filters:
- variable: INPUT
detection: go_shared_lang_dynamic_request_input
scope: cursor
- pattern: $<FORMAT_STRING>$<...>$<INPUT>$<...>;
filters:
- not:
variable: FORMAT_STRING
string_regex: /A*?(\%q)*?/z
- variable: INPUT
detection: go_shared_lang_dynamic_request_input
scope: cursor
- id: go_lang_log_output_neutralization_sanitizer
patterns:
- strconv.Quote();
- html.EscapeString();
- url.QueryEscape();
- strings.ReplaceAll();
languages:
- go
metadata:
description: Missing output neutralization for logs
remediation_message: |
## Description

Logging untrusted and unsanitized input could lead to log injection vulnerabilities.

## Remediations

❌ Do not log unsanitized external input directly

✅ Use printf methods with %q format for logging external input

```go
dangerousInput := os.Args[0]

logger.Printf("Args: %q", dangerousInput)
```

✅ Use manual method to escape external strings before logging

```go
dangerousInput := os.Args[0]
sanitizedInput := strconv.Quote(dangerousInput)

logger.Print(sanitizedInput)
```
cwe_id:
- 117
id: go_lang_log_output_neutralization
documentation_url: https://docs.bearer.com/reference/rules/go_lang_log_output_neutralization
18 changes: 18 additions & 0 deletions tests/go/lang/log_output_neutralization/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const {
createNewInvoker,
getEnvironment,
} = require("../../../helper.js")
const { ruleId, ruleFile, testBase } = getEnvironment(__dirname)

describe(ruleId, () => {
const invoke = createNewInvoker(ruleId, ruleFile, testBase)

test("log_output_neutralization", () => {
const testCase = "main.go"

const results = invoke(testCase)

expect(results.Missing).toEqual([])
expect(results.Extra).toEqual([])
})
})
73 changes: 73 additions & 0 deletions tests/go/lang/log_output_neutralization/testdata/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Use bearer:expected go_lang_log_output_neutralization to flag expected findings

package main

import (
"bytes"
"os"

"log"

seelog "github.com/cihub/seelog"
glog "github.com/golang/glog"
zerolog "github.com/rs/zerolog"
logrus "github.com/sirupsen/logrus"
"go.uber.org/zap"

"strconv"
)

func bad() {
var buf bytes.Buffer
logger := log.New(&buf, "logger: ", log.Lshortfile)

// bearer:expected go_lang_log_output_neutralization
logger.Print(os.Args[0])
}

func bad2() {
// bearer:expected go_lang_log_output_neutralization
zerolog.Info.Msg(os.Args[0])
// bearer:expected go_lang_log_output_neutralization
zerolog.Print(os.Args[0])
}

func bad3() {
// bearer:expected go_lang_log_output_neutralization
logrus.WithFields().Info(os.Args[0])

log := logrus.New()
// bearer:expected go_lang_log_output_neutralization
log.Error(os.Args[0])
}

func bad4() {
var cfg zap.Config
logger := zap.Must(cfg.Build())
defer logger.Sync()

// bearer:expected go_lang_log_output_neutralization
logger.Info(os.Args[0])
logger.Printf("Args: %s", os.Args[0])
}

func bad5() {
// bearer:expected go_lang_log_output_neutralization
seelog.Trace(os.Args[0])
}

func bad6() {
// bearer:expected go_lang_log_output_neutralization
glog.Fatalf(os.Args[0])
}

func ok(input string) {
var buf bytes.Buffer
logger := log.New(&buf, "logger: ", log.Lshortfile)
sanitizedInput := strconv.Quote(input)

logger.Print(sanitizedInput)
logger.Printf("Args: %q", os.Args[0])

zerolog.Info.Msg(sanitizedInput)
}
Loading