From f5bb22ce481fa0eb8a5f2a8dd8d7abeb868dd4f3 Mon Sep 17 00:00:00 2001 From: kotakanbe Date: Mon, 16 Mar 2026 13:08:14 +0900 Subject: [PATCH 1/4] deps: remove lfshook in favor of local fileHook implementation Replace github.com/rifflock/lfshook with a minimal fileHook that implements logrus.Hook. The hook writes log entries to a file using logrus.TextFormatter. lfshook is now fully removed from the module. Co-Authored-By: Claude Opus 4.6 --- go.mod | 1 - go.sum | 2 -- logging/logutil.go | 37 ++++++++++++++++++++++++++++--------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index c793fa3a72..4fa8ea21df 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,6 @@ require ( github.com/package-url/packageurl-go v0.1.5 github.com/parnurzeal/gorequest v0.3.0 github.com/pkg/errors v0.9.1 - github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/samber/lo v1.52.0 github.com/sirupsen/logrus v1.9.4 diff --git a/go.sum b/go.sum index 4543b8bbec..b2a2321a68 100644 --- a/go.sum +++ b/go.sum @@ -770,8 +770,6 @@ github.com/redis/rueidis v1.0.70 h1:O01v0Mt27/qXV9mKU/zahgxHdC8piHzIepqW4Nyzn/I= github.com/redis/rueidis v1.0.70/go.mod h1:lfdcZzJ1oKGKL37vh9fO3ymwt+0TdjkkUCJxbgpmcgQ= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= -github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= diff --git a/logging/logutil.go b/logging/logutil.go index 22752f65b9..3b9f18990b 100644 --- a/logging/logutil.go +++ b/logging/logutil.go @@ -9,12 +9,35 @@ import ( "runtime" "github.com/k0kubun/pp" - "github.com/rifflock/lfshook" "github.com/sirupsen/logrus" formatter "github.com/kotakanbe/logrus-prefixed-formatter" ) +type fileHook struct { + path string + formatter logrus.Formatter +} + +func (h *fileHook) Levels() []logrus.Level { + return logrus.AllLevels +} + +func (h *fileHook) Fire(entry *logrus.Entry) error { + f, err := os.OpenFile(h.path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + return err + } + defer f.Close() + + b, err := h.formatter.Format(entry) + if err != nil { + return err + } + _, err = f.Write(b) + return err +} + // LogOpts has options for logging type LogOpts struct { Debug bool `json:"debug,omitempty"` @@ -94,14 +117,10 @@ func NewCustomLogger(debug, quiet, logToFile bool, logDir, logMsgAnsiColor, serv if _, err := os.Stat(dir); err == nil { path := filepath.Join(dir, fmt.Sprintf("%s.log", whereami)) if _, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil { - log.Hooks.Add(lfshook.NewHook(lfshook.PathMap{ - logrus.DebugLevel: path, - logrus.InfoLevel: path, - logrus.WarnLevel: path, - logrus.ErrorLevel: path, - logrus.FatalLevel: path, - logrus.PanicLevel: path, - }, nil)) + log.Hooks.Add(&fileHook{ + path: path, + formatter: &logrus.TextFormatter{DisableColors: true}, + }) } else { log.Errorf("Failed to create log file. path: %s, err: %+v", path, err) } From 0ecc1dc43a0a5402e0f765a8c4d554b464a04a6e Mon Sep 17 00:00:00 2001 From: kotakanbe Date: Mon, 16 Mar 2026 13:15:29 +0900 Subject: [PATCH 2/4] test: add fileHook tests for lfshook replacement Verify that fileHook correctly implements logrus.Hook interface, writes formatted entries to file, and appends on multiple calls. Co-Authored-By: Claude Opus 4.6 --- logging/filehook_test.go | 125 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 logging/filehook_test.go diff --git a/logging/filehook_test.go b/logging/filehook_test.go new file mode 100644 index 0000000000..8cd3b9bde1 --- /dev/null +++ b/logging/filehook_test.go @@ -0,0 +1,125 @@ +package logging + +import ( + "os" + "strings" + "testing" + + "github.com/sirupsen/logrus" +) + +func TestFileHookLevels(t *testing.T) { + h := &fileHook{ + path: "/dev/null", + formatter: &logrus.TextFormatter{}, + } + got := h.Levels() + if len(got) != len(logrus.AllLevels) { + t.Errorf("Levels() returned %d levels, want %d", len(got), len(logrus.AllLevels)) + } + for i, l := range logrus.AllLevels { + if got[i] != l { + t.Errorf("Levels()[%d] = %v, want %v", i, got[i], l) + } + } +} + +func TestFileHookFire(t *testing.T) { + tmpFile, err := os.CreateTemp(t.TempDir(), "filehook-*.log") + if err != nil { + t.Fatalf("failed to create temp file: %v", err) + } + tmpFile.Close() + + h := &fileHook{ + path: tmpFile.Name(), + formatter: &logrus.TextFormatter{DisableColors: true, DisableTimestamp: true}, + } + + entry := &logrus.Entry{ + Logger: logrus.New(), + Level: logrus.InfoLevel, + Message: "hello from fire", + Data: logrus.Fields{}, + } + + if err := h.Fire(entry); err != nil { + t.Fatalf("Fire() returned error: %v", err) + } + + content, err := os.ReadFile(tmpFile.Name()) + if err != nil { + t.Fatalf("failed to read log file: %v", err) + } + if !strings.Contains(string(content), "hello from fire") { + t.Errorf("log file does not contain expected message, got: %q", string(content)) + } +} + +func TestFileHookFireAppends(t *testing.T) { + tmpFile, err := os.CreateTemp(t.TempDir(), "filehook-*.log") + if err != nil { + t.Fatalf("failed to create temp file: %v", err) + } + tmpFile.Close() + + h := &fileHook{ + path: tmpFile.Name(), + formatter: &logrus.TextFormatter{DisableColors: true, DisableTimestamp: true}, + } + + for i, msg := range []string{"first message", "second message", "third message"} { + entry := &logrus.Entry{ + Logger: logrus.New(), + Level: logrus.InfoLevel, + Message: msg, + Data: logrus.Fields{}, + } + if err := h.Fire(entry); err != nil { + t.Fatalf("Fire() call %d returned error: %v", i, err) + } + } + + content, err := os.ReadFile(tmpFile.Name()) + if err != nil { + t.Fatalf("failed to read log file: %v", err) + } + s := string(content) + for _, msg := range []string{"first message", "second message", "third message"} { + if !strings.Contains(s, msg) { + t.Errorf("log file missing %q, got: %q", msg, s) + } + } +} + +func TestFileHookIntegration(t *testing.T) { + tmpFile, err := os.CreateTemp(t.TempDir(), "filehook-*.log") + if err != nil { + t.Fatalf("failed to create temp file: %v", err) + } + tmpFile.Close() + + logger := logrus.New() + logger.Out = os.Stderr + logger.Level = logrus.DebugLevel + + logger.Hooks.Add(&fileHook{ + path: tmpFile.Name(), + formatter: &logrus.TextFormatter{DisableColors: true, DisableTimestamp: true}, + }) + + logger.Info("integration info message") + logger.Warn("integration warn message") + + content, err := os.ReadFile(tmpFile.Name()) + if err != nil { + t.Fatalf("failed to read log file: %v", err) + } + s := string(content) + if !strings.Contains(s, "integration info message") { + t.Errorf("log file missing info message, got: %q", s) + } + if !strings.Contains(s, "integration warn message") { + t.Errorf("log file missing warn message, got: %q", s) + } +} From fa611980379f7eb8f9793d5b2672ef10c61eccf7 Mon Sep 17 00:00:00 2001 From: kotakanbe Date: Tue, 17 Mar 2026 09:23:19 +0900 Subject: [PATCH 3/4] fix: handle errcheck lint for tmpFile.Close in tests Co-Authored-By: Claude Opus 4.6 --- logging/filehook_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/logging/filehook_test.go b/logging/filehook_test.go index 8cd3b9bde1..ce5aa8f9e9 100644 --- a/logging/filehook_test.go +++ b/logging/filehook_test.go @@ -29,7 +29,9 @@ func TestFileHookFire(t *testing.T) { if err != nil { t.Fatalf("failed to create temp file: %v", err) } - tmpFile.Close() + if err := tmpFile.Close(); err != nil { + t.Fatalf("failed to close temp file: %v", err) + } h := &fileHook{ path: tmpFile.Name(), @@ -61,7 +63,9 @@ func TestFileHookFireAppends(t *testing.T) { if err != nil { t.Fatalf("failed to create temp file: %v", err) } - tmpFile.Close() + if err := tmpFile.Close(); err != nil { + t.Fatalf("failed to close temp file: %v", err) + } h := &fileHook{ path: tmpFile.Name(), @@ -97,7 +101,9 @@ func TestFileHookIntegration(t *testing.T) { if err != nil { t.Fatalf("failed to create temp file: %v", err) } - tmpFile.Close() + if err := tmpFile.Close(); err != nil { + t.Fatalf("failed to close temp file: %v", err) + } logger := logrus.New() logger.Out = os.Stderr From 55e95fff7d6181ddd75561651602ee9c69713d9a Mon Sep 17 00:00:00 2001 From: vuls-bot Date: Tue, 24 Mar 2026 02:08:08 +0000 Subject: [PATCH 4/4] fix: address Copilot review on lfshook removal - Close FD leak in NewCustomLogger (checkFile.Close()) - Use os.DevNull instead of /dev/null in test for portability - Tighten file permissions from 0644 to 0600 for all log files Co-Authored-By: Claude Opus 4.6 (1M context) --- logging/filehook_test.go | 2 +- logging/logutil.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/logging/filehook_test.go b/logging/filehook_test.go index ce5aa8f9e9..b04cb28c60 100644 --- a/logging/filehook_test.go +++ b/logging/filehook_test.go @@ -10,7 +10,7 @@ import ( func TestFileHookLevels(t *testing.T) { h := &fileHook{ - path: "/dev/null", + path: os.DevNull, formatter: &logrus.TextFormatter{}, } got := h.Levels() diff --git a/logging/logutil.go b/logging/logutil.go index 3b9f18990b..e4a90edd1a 100644 --- a/logging/logutil.go +++ b/logging/logutil.go @@ -24,7 +24,7 @@ func (h *fileHook) Levels() []logrus.Level { } func (h *fileHook) Fire(entry *logrus.Entry) error { - f, err := os.OpenFile(h.path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + f, err := os.OpenFile(h.path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return err } @@ -107,7 +107,7 @@ func NewCustomLogger(debug, quiet, logToFile bool, logDir, logMsgAnsiColor, serv } logFile := dir + "/vuls.log" - if file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil { + if file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600); err == nil { log.Out = io.MultiWriter(os.Stderr, file) } else { log.Out = os.Stderr @@ -116,7 +116,8 @@ func NewCustomLogger(debug, quiet, logToFile bool, logDir, logMsgAnsiColor, serv if _, err := os.Stat(dir); err == nil { path := filepath.Join(dir, fmt.Sprintf("%s.log", whereami)) - if _, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil { + if checkFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600); err == nil { + checkFile.Close() log.Hooks.Add(&fileHook{ path: path, formatter: &logrus.TextFormatter{DisableColors: true},