feat: create analyzer

pull/7/head
sundowndev 2022-10-31 18:59:20 +04:00
parent cec318a935
commit c580f0b9e4
11 changed files with 207 additions and 80 deletions

View File

@ -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
}

View File

@ -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(),
)
}

60
lib/analysis/analyzer.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -2,4 +2,6 @@ package services
func Init() {
AddService(NewSSHdService())
AddService(NewLastLogService())
AddService(NewShellHistoryService())
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)
}