feat: create analyzer
parent
cec318a935
commit
c580f0b9e4
41
cmd/root.go
41
cmd/root.go
|
@ -6,17 +6,14 @@ import (
|
|||
"github.com/sundowndev/covermyass/v2/build"
|
||||
"github.com/sundowndev/covermyass/v2/lib/analysis"
|
||||
"github.com/sundowndev/covermyass/v2/lib/filter"
|
||||
"github.com/sundowndev/covermyass/v2/lib/find"
|
||||
"github.com/sundowndev/covermyass/v2/lib/output"
|
||||
"github.com/sundowndev/covermyass/v2/lib/services"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type RootCmdOptions struct {
|
||||
List bool
|
||||
Write bool
|
||||
List bool
|
||||
Write bool
|
||||
ExcludeReadOnly bool
|
||||
//ExtraPaths []string
|
||||
//FilterRules []string
|
||||
}
|
||||
|
@ -37,37 +34,18 @@ func NewRootCmd() *cobra.Command {
|
|||
output.ChangePrinter(output.NewConsolePrinter())
|
||||
}
|
||||
|
||||
a := analysis.New()
|
||||
|
||||
output.Printf("Loading known log files for %s\n", runtime.GOOS)
|
||||
services.Init()
|
||||
for _, service := range services.Services() {
|
||||
patterns, ok := service.Paths()[runtime.GOOS]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
a.AddPatterns(patterns...)
|
||||
}
|
||||
|
||||
filterEngine := filter.NewEngine()
|
||||
finder := find.New(os.DirFS(""), filterEngine, a.Patterns())
|
||||
|
||||
output.Printf("Searching for log files...\n\n")
|
||||
err := finder.Run()
|
||||
analyzer := analysis.NewAnalyzer(filterEngine)
|
||||
a, err := analyzer.Analyze()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, info := range finder.Results() {
|
||||
a.AddResult(analysis.Result{
|
||||
Path: info.Path(),
|
||||
Size: info.Size(),
|
||||
Mode: info.Mode(),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.List {
|
||||
for _, result := range a.Results() {
|
||||
if opts.ExcludeReadOnly && result.ReadOnly {
|
||||
continue
|
||||
}
|
||||
fmt.Println(result.Path)
|
||||
}
|
||||
return nil
|
||||
|
@ -81,6 +59,7 @@ func NewRootCmd() *cobra.Command {
|
|||
|
||||
cmd.PersistentFlags().BoolVarP(&opts.List, "list", "l", false, "Show files in a simple list format. This will prevent any write operation.")
|
||||
cmd.PersistentFlags().BoolVar(&opts.Write, "write", false, "Erase found log files. This WILL truncate the files!")
|
||||
cmd.PersistentFlags().BoolVar(&opts.ExcludeReadOnly, "no-read-only", false, "Exclude read-only files in the list. Must be used with --list.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -10,13 +10,14 @@ import (
|
|||
type Summary struct {
|
||||
TotalFiles int
|
||||
TotalRWFiles int
|
||||
TotalROFiles int
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Path string
|
||||
Size int64
|
||||
Mode os.FileMode
|
||||
Service string
|
||||
Path string
|
||||
Size int64
|
||||
Mode os.FileMode
|
||||
ReadOnly bool
|
||||
}
|
||||
|
||||
type Analysis struct {
|
||||
|
@ -26,7 +27,7 @@ type Analysis struct {
|
|||
results []Result
|
||||
}
|
||||
|
||||
func New() *Analysis {
|
||||
func NewAnalysis() *Analysis {
|
||||
return &Analysis{
|
||||
Date: time.Now(),
|
||||
summary: Summary{},
|
||||
|
@ -35,17 +36,12 @@ func New() *Analysis {
|
|||
}
|
||||
}
|
||||
|
||||
func (a *Analysis) AddPatterns(patterns ...string) {
|
||||
a.patterns = append(a.patterns, patterns...)
|
||||
}
|
||||
|
||||
func (a *Analysis) Patterns() []string {
|
||||
return a.patterns
|
||||
}
|
||||
|
||||
func (a *Analysis) AddResult(result Result) {
|
||||
a.results = append(a.results, result)
|
||||
a.summary.TotalFiles += 1
|
||||
if !result.ReadOnly {
|
||||
a.summary.TotalRWFiles += 1
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Analysis) Results() []Result {
|
||||
|
@ -57,17 +53,17 @@ func (a *Analysis) Write(w io.Writer) {
|
|||
_, _ = fmt.Fprintf(w, "Found the following files\n")
|
||||
|
||||
for _, res := range a.results {
|
||||
_, _ = fmt.Fprintf(w, "%s (%s)\n", res.Path, byteCountSI(res.Size))
|
||||
_, _ = fmt.Fprintf(w, "%s (%s, %s)\n", res.Path, byteCountSI(res.Size), res.Mode.String())
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "\n")
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(
|
||||
w,
|
||||
"Summary\nFound %d files (%d RW, %d RO) in %s\n",
|
||||
"Summary\nFound %d files (%d read-write, %d read-only) in %s\n",
|
||||
a.summary.TotalFiles,
|
||||
a.summary.TotalRWFiles,
|
||||
a.summary.TotalROFiles,
|
||||
a.summary.TotalFiles-a.summary.TotalRWFiles,
|
||||
time.Since(a.Date).Round(time.Millisecond).String(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package analysis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/sundowndev/covermyass/v2/lib/filter"
|
||||
"github.com/sundowndev/covermyass/v2/lib/find"
|
||||
"github.com/sundowndev/covermyass/v2/lib/output"
|
||||
"github.com/sundowndev/covermyass/v2/lib/services"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Analyzer struct {
|
||||
filter filter.Filter
|
||||
}
|
||||
|
||||
func NewAnalyzer(filterEngine filter.Filter) *Analyzer {
|
||||
return &Analyzer{filterEngine}
|
||||
}
|
||||
|
||||
func (a *Analyzer) Analyze() (*Analysis, error) {
|
||||
analysis := NewAnalysis()
|
||||
|
||||
output.Printf("Loading known log files for %s\n", runtime.GOOS)
|
||||
services.Init()
|
||||
output.Printf("Scanning file system...\n\n")
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
m := &sync.Mutex{}
|
||||
for _, service := range services.Services() {
|
||||
wg.Add(1)
|
||||
go func(svc services.Service) {
|
||||
finder := find.New(os.DirFS(""), a.filter, svc.Paths())
|
||||
if err := finder.Run(context.TODO()); err != nil {
|
||||
logrus.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
for _, info := range finder.Results() {
|
||||
analysis.AddResult(Result{
|
||||
Service: svc.Name(),
|
||||
Path: info.Path(),
|
||||
Size: info.Size(),
|
||||
Mode: info.Mode(),
|
||||
ReadOnly: info.ReadOnly(),
|
||||
})
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}(service)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return analysis, nil
|
||||
}
|
|
@ -1,17 +1,30 @@
|
|||
package find
|
||||
|
||||
import "io/fs"
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
type FileInfo interface {
|
||||
fs.FileInfo
|
||||
Path() string
|
||||
ReadOnly() bool
|
||||
}
|
||||
|
||||
type fileInfo struct {
|
||||
fs.FileInfo
|
||||
path string
|
||||
ro bool
|
||||
}
|
||||
|
||||
func (f *fileInfo) Path() string {
|
||||
return f.path
|
||||
}
|
||||
|
||||
func (f *fileInfo) ReadOnly() bool {
|
||||
_, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
f.ro = true
|
||||
}
|
||||
return f.ro
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package find
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bmatcuk/doublestar/v4"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -12,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
type Finder interface {
|
||||
Run() error
|
||||
Run(context.Context) error
|
||||
Results() []FileInfo
|
||||
}
|
||||
|
||||
|
@ -32,16 +33,20 @@ func New(fsys fs.FS, filterEngine filter.Filter, paths []string) Finder {
|
|||
}
|
||||
}
|
||||
|
||||
func (f *finder) Run() error {
|
||||
func (f *finder) Run(ctx context.Context) error {
|
||||
// Voluntary reset the results slice
|
||||
f.results = make([]FileInfo, 0)
|
||||
|
||||
for _, pattern := range f.paths {
|
||||
if len(pattern) == 0 {
|
||||
logrus.Warn("pattern skipped because it has lengh of 0")
|
||||
logrus.Warn("pattern skipped because it has length of 0")
|
||||
continue
|
||||
}
|
||||
|
||||
if !doublestar.ValidatePathPattern(pattern) {
|
||||
return fmt.Errorf("pattern %s is not valid", pattern)
|
||||
}
|
||||
|
||||
var formattedPattern string
|
||||
if strings.Split(pattern, "")[0] == string(os.PathSeparator) {
|
||||
formattedPattern = strings.Join(strings.Split(pattern, "")[1:], "")
|
||||
|
@ -53,7 +58,10 @@ func (f *finder) Run() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.results = append(f.results, &fileInfo{info, fmt.Sprintf("%s%s", string(os.PathSeparator), filepath.FromSlash(path))})
|
||||
f.results = append(f.results, &fileInfo{
|
||||
FileInfo: info,
|
||||
path: fmt.Sprintf("%s%s", string(os.PathSeparator), filepath.FromSlash(path)),
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/sundowndev/covermyass/v2/lib/find"
|
||||
"os"
|
||||
)
|
||||
|
||||
type ShellHistoryService struct{}
|
||||
|
||||
func NewShellHistoryService() Service {
|
||||
return &ShellHistoryService{}
|
||||
}
|
||||
|
||||
func (s *ShellHistoryService) Name() string {
|
||||
return "shell_history"
|
||||
}
|
||||
|
||||
func (s *ShellHistoryService) Paths() []string {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return []string{}
|
||||
}
|
||||
return []string{
|
||||
fmt.Sprintf("%s/.bash_history", homeDir),
|
||||
fmt.Sprintf("%s/.zsh_history", homeDir),
|
||||
fmt.Sprintf("%s/.node_repl_history", homeDir),
|
||||
fmt.Sprintf("%s/.python_history", homeDir),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ShellHistoryService) HandleFile(file find.FileInfo) error {
|
||||
return os.Truncate(file.Path(), 0)
|
||||
}
|
|
@ -2,4 +2,6 @@ package services
|
|||
|
||||
func Init() {
|
||||
AddService(NewSSHdService())
|
||||
AddService(NewLastLogService())
|
||||
AddService(NewShellHistoryService())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
//go:build !windows
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/sundowndev/covermyass/v2/lib/find"
|
||||
"os"
|
||||
)
|
||||
|
||||
type LastLogService struct{}
|
||||
|
||||
func NewLastLogService() Service {
|
||||
return &LastLogService{}
|
||||
}
|
||||
|
||||
func (s *LastLogService) Name() string {
|
||||
return "lastlog"
|
||||
}
|
||||
|
||||
func (s *LastLogService) Paths() []string {
|
||||
return []string{
|
||||
"/var/log/lastlog",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LastLogService) HandleFile(file find.FileInfo) error {
|
||||
return os.Truncate(file.Path(), 0)
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package services
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"github.com/sundowndev/covermyass/v2/lib/find"
|
||||
)
|
||||
|
||||
const (
|
||||
Linux = "linux"
|
||||
|
@ -12,8 +14,8 @@ var services []Service
|
|||
|
||||
type Service interface {
|
||||
Name() string
|
||||
Paths() map[string][]string
|
||||
HandleFile(string, os.FileInfo) error
|
||||
Paths() []string
|
||||
HandleFile(find.FileInfo) error
|
||||
}
|
||||
|
||||
func Services() []Service {
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package services
|
||||
|
||||
import "os"
|
||||
|
||||
type SSHdService struct{}
|
||||
|
||||
func NewSSHdService() Service {
|
||||
return &SSHdService{}
|
||||
}
|
||||
|
||||
func (s *SSHdService) Name() string {
|
||||
return "sshd"
|
||||
}
|
||||
|
||||
func (s *SSHdService) Paths() map[string][]string {
|
||||
return map[string][]string{
|
||||
Linux: {
|
||||
"/var/log/sshd.log",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSHdService) HandleFile(path string, info os.FileInfo) error {
|
||||
return os.Truncate(path, 0)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//go:build !windows
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/sundowndev/covermyass/v2/lib/find"
|
||||
"os"
|
||||
)
|
||||
|
||||
type SSHdService struct{}
|
||||
|
||||
func NewSSHdService() Service {
|
||||
return &SSHdService{}
|
||||
}
|
||||
|
||||
func (s *SSHdService) Name() string {
|
||||
return "sshd"
|
||||
}
|
||||
|
||||
func (s *SSHdService) Paths() []string {
|
||||
return []string{
|
||||
"/var/log/sshd.log",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSHdService) HandleFile(file find.FileInfo) error {
|
||||
return os.Truncate(file.Path(), 0)
|
||||
}
|
Loading…
Reference in New Issue