Skip to content

Commit

Permalink
unit testing and v2.0.0 release
Browse files Browse the repository at this point in the history
  • Loading branch information
codeyourweb committed Jan 30, 2022
1 parent 4003c90 commit 3674dd0
Show file tree
Hide file tree
Showing 14 changed files with 267 additions and 47 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/go_build_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,4 @@ jobs:
go build -trimpath -tags yara_static -a -ldflags '-s -w -extldflags "-static"' .
ls
sudo chmod +x fastfinder
sudo ./fastfinder -h
- name: Tests
run: |
export PKG_CONFIG_PATH=${HOME}/prefix/lib/pkgconfig
go test -v -tags yara_static -a -ldflags '-s -w -extldflags "-static"' .
sudo ./fastfinder -h
15 changes: 1 addition & 14 deletions .github/workflows/go_build_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,4 @@ jobs:
cd $Env:GITHUB_WORKSPACE
go build -trimpath -tags yara_static -a -ldflags '-s -w -extldflags "-static"' .
ls
.\fastfinder.exe -h
- name: Tests
shell: powershell
run: |
$Env:PATH += ";C:/msys64/mingw64/include"
$Env:PATH += ";C:/msys64/mingw64/lib"
$Env:PATH += ";C:/msys64/mingw64/lib/pkgconfig"
$Env:GOOS="windows"
$Env:GOARCH="amd64"
$Env:CGO_CFLAGS="-IC:/msys64/mingw64/include"
$Env:CGO_LDFLAGS="-LC:/msys64/mingw64/lib -lyara -lcrypto"
$Env:PKG_CONFIG_PATH="C:/msys64/mingw64/lib/pkgconfig"
cd $Env:GITHUB_WORKSPACE
go test -v
.\fastfinder.exe -h
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ focused on endpoint enumeration and suspicious file finding based on various cri
* complex content condition(s) based on YARA

## Ready for battle!
* fastfinder has been tested in real cases in multiple CERT, CSIRT and SOC
* examples directory now include real malwares , suspect behaviors or vulnerability scan
* fastfinder has been tested in real cases in multiple CERT, CSIRT and SOC use cases
* examples directory now include real malwares / suspect behaviors or vulnerability scan examples

### Installation
Compiled release of this software are available. If you want to compile
Expand Down Expand Up @@ -83,7 +83,7 @@ advancedparameters:
```
### Search everywhere or in specified paths:
* use '?' in paths for simple char wildcard (eg. powershe??.exe)
* use '\\*' in paths for multiple chars wildcard (eg. \\*.exe)
* use '\\\*' in paths for multiple chars wildcard (eg. \\\*.exe)
* regular expressions are also available , just enclose paths with slashes (eg. /[0-9]{8}\\.exe/)
* environment variables can also be used (eg. %TEMP%\\myfile.exe)

Expand Down
6 changes: 4 additions & 2 deletions gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ var txtStdout *tview.TextView
var txtStderr *tview.TextView
var UIselectedConfigPath string
var UItmpConfigPath string
var currentMainWindowSelector int = 1
var currentConfigWindowSelector int = 1
var currentMainWindowSelector int = 0
var currentConfigWindowSelector int = 0

func InitUI() {
UIapp = tview.NewApplication()
Expand All @@ -31,6 +31,7 @@ func InitUI() {

// MainWindow display application UI
func MainWindow() {
currentMainWindowSelector = 1
/*
* TEXTVIEW : Windows app name
*/
Expand Down Expand Up @@ -120,6 +121,7 @@ func MainWindow() {

// OpenFileDialog show a navigable tree view of the current directory.
func OpenFileDialog() {
currentConfigWindowSelector = 1
/*
* TEXTVIEW : Dialog title
*/
Expand Down
21 changes: 16 additions & 5 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ const (
var loggingVerbosity int = 3
var loggingPath string = ""
var loggingFile *os.File
var unitTesting bool

func LogTesting(testing bool) {
unitTesting = testing

if !testing {
log.SetOutput(os.Stderr)
}
}

// LogMessage output message to the specific standard / error output
func LogMessage(logType int, logMessage ...interface{}) {
Expand All @@ -29,7 +38,7 @@ func LogMessage(logType int, logMessage ...interface{}) {

message := strings.Join(aString, " ")

if UIactive && AppStarted {
if UIactive && AppStarted && !unitTesting {
currentTime := time.Now()
message = "[" + currentTime.Format("2006-01-02 15:04:05") + "] " + message
if logType == LOG_INFO || logType == LOG_VERBOSE || logType == LOG_EXIT {
Expand All @@ -43,10 +52,12 @@ func LogMessage(logType int, logMessage ...interface{}) {
fmt.Fprintf(txtStderr, "%s\n", message)
}
} else {
if logType == LOG_ERROR {
log.SetOutput(os.Stderr)
} else {
log.SetOutput(os.Stdout)
if !unitTesting {
if logType == LOG_ERROR {
log.SetOutput(os.Stderr)
} else {
log.SetOutput(os.Stdout)
}
}

log.Println(message)
Expand Down
37 changes: 21 additions & 16 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ import (
"github.com/hillu/go-yara/v4"
)

const FASTFINDER_VERSION = "2.0.0b"
const FASTFINDER_VERSION = "2.0.0"
const YARA_VERSION = "4.1.3"
const BUILDER_RC4_KEY = ">Õ°ªKb{¡§ÌB$lMÕ±9l.tòÑé¦Ø¿"

func main() {
// parse configuration file
parser := argparse.NewParser("fastfinder", "Incident Response - Fast suspicious file finder")
parser := argparse.NewParser("fastfinder", "Fastfinder v"+FASTFINDER_VERSION+" (with YARA "+YARA_VERSION+")"+LineBreak+"\t\t\tIncident Response - Fast suspicious file finder")
pConfigPath := parser.String("c", "configuration", &argparse.Options{Required: false, Default: "", Help: "Fastfind configuration file"})
pSfxPath := parser.String("b", "build", &argparse.Options{Required: false, Help: "Output a standalone package with configuration and rules in a single binary"})
pOutLogPath := parser.String("o", "output", &argparse.Options{Required: false, Help: "Save fastfinder logs in the specified file"})
Expand All @@ -42,59 +42,64 @@ func main() {
log.Fatal(parser.Usage(err))
}

RunProgramWithParameters(*pConfigPath, *pSfxPath, *pOutLogPath, *pHideWindow, *pDisableAdvUI, *pLogVerbosity, *pTriage)
}

// RunProgramWithParameters used specified argv and run fastfinder
func RunProgramWithParameters(pConfigPath string, pSfxPath string, pOutLogPath string, pHideWindow bool, pDisableAdvUI bool, pLogVerbosity int, pTriage bool) {
// enable advanced UI
if *pTriage || *pDisableAdvUI || *pHideWindow || len(*pSfxPath) > 0 {
if pTriage || pDisableAdvUI || pHideWindow || len(pSfxPath) > 0 {
UIactive = false
} else {
InitUI()
}

// display open file dialog when config file empty
if len(*pConfigPath) == 0 {
if len(pConfigPath) == 0 {
InitUI()
OpenFileDialog()
*pConfigPath = UIselectedConfigPath
pConfigPath = UIselectedConfigPath
}

// check for log path validity
if len(*pOutLogPath) > 0 {
if strings.Contains(*pOutLogPath, " ") {
if len(pOutLogPath) > 0 {
if strings.Contains(pOutLogPath, " ") {
LogFatal("Log file path cannot contain spaces")
}
}

// init progressbar object
EnableProgressbar(*pDisableAdvUI)
EnableProgressbar(pDisableAdvUI)

// configuration parsing
var config Configuration
config.getConfiguration(*pConfigPath)
config.getConfiguration(pConfigPath)
if config.Output.FilesCopyPath != "" {
config.Output.FilesCopyPath = "./"
}

// window hidden
if *pHideWindow && len(*pSfxPath) == 0 {
if pHideWindow && len(pSfxPath) == 0 {
HideConsoleWindow()
}

// output log to file
if len(*pOutLogPath) > 0 && len(*pSfxPath) == 0 {
loggingPath = *pOutLogPath
if len(pOutLogPath) > 0 && len(pSfxPath) == 0 {
loggingPath = pOutLogPath
}

// file logging verbosity
if *pLogVerbosity >= 1 && *pLogVerbosity <= 4 {
loggingVerbosity = *pLogVerbosity
if pLogVerbosity >= 1 && pLogVerbosity <= 4 {
loggingVerbosity = pLogVerbosity
}

// run app
if UIactive {
go MainFastfinderRoutine(config, *pConfigPath, *pDisableAdvUI, *pHideWindow, *pSfxPath, *pTriage, *pOutLogPath, *pLogVerbosity)
go MainFastfinderRoutine(config, pConfigPath, pDisableAdvUI, pHideWindow, pSfxPath, pTriage, pOutLogPath, pLogVerbosity)
MainWindow()
} else {
LogMessage(LOG_INFO, LineBreak+"================================================"+LineBreak+RenderFastfinderLogo()+"================================================"+LineBreak)
MainFastfinderRoutine(config, *pConfigPath, *pDisableAdvUI, *pHideWindow, *pSfxPath, *pTriage, *pOutLogPath, *pLogVerbosity)
MainFastfinderRoutine(config, pConfigPath, pDisableAdvUI, pHideWindow, pSfxPath, pTriage, pOutLogPath, pLogVerbosity)
}

}
Expand Down
70 changes: 70 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"os"
"os/exec"
"runtime"
"testing"
"time"

"github.com/rivo/tview"
)

func TestConfigWindow(t *testing.T) {
InitUI()
go OpenFileDialog()
time.Sleep(500 * time.Millisecond)

if currentConfigWindowSelector == 0 {
t.Fatal("OpenFileDialog failed to open")
}
}

func TestMainWindow(t *testing.T) {
InitUI()
go MainWindow()
time.Sleep(500 * time.Millisecond)

if currentMainWindowSelector == 0 {
t.Fatal("MainWindow failed to open")
}
}

func TestConfigurationFileLoading(t *testing.T) {
var config Configuration
config.getConfiguration("tests/config_test_standard.yml")

if len(config.Input.Content.Grep) == 0 || config.Input.Content.Grep[0] != "package main" {
t.Fatal("config.getConfiguration fails to load and parse configuration file correctly")
}
}

func TestRC4CipheredConfigurationFileLoading(t *testing.T) {
var config Configuration
config.getConfiguration("tests/config_test_ciphered.yml")

if len(config.Input.Content.Grep) == 0 || config.Input.Content.Grep[0] != "package main" {
t.Fatal("config.getConfiguration fails to load and parse configuration file correctly")
}
}

func TestCleanUI(t *testing.T) {
UIapp = tview.NewApplication()
UIapp.ForceDraw()
UIactive = false
AppStarted = false
UIapp.Stop()
if UIactive || AppStarted {
t.Fatal("Can't reset GUI app for further testing")
}

if runtime.GOOS == "linux" {
cmd := exec.Command("clear")
cmd.Stdout = os.Stdout
cmd.Run()
} else {
cmd := exec.Command("cmd", "/c", "cls")
cmd.Stdout = os.Stdout
cmd.Run()
}
}
Binary file added tests/config_test_ciphered.yml
Binary file not shown.
17 changes: 17 additions & 0 deletions tests/config_test_standard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
input:
path: []
content:
grep:
- 'package main'
yara: []
checksum: []
options:
contentMatchDependsOnPathMatch: false
findInHardDrives: true
findInRemovableDrives: false
findInNetworkDrives: false
findInCDRomDrives: false
output:
copyMatchingFiles: false
base64Files: false
filesCopyPath: ''
1 change: 1 addition & 0 deletions tests/rule_test_ciphered.yar
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
씏�⑵ꬔ弼錭龮휐䁻⮉쌟纘፴뽬笠薡�脑洪䱍ꖣ⼹鞓뙂튨韉我≖洽솎랑芅ꊾ㌎ﯕ穚遌Ꝉ
6 changes: 6 additions & 0 deletions tests/rule_test_standard.yar
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
rule testing{
strings:
$ = "TestFindInFilesContent"
condition:
all of them
}
6 changes: 4 additions & 2 deletions utils_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ func ListDirectoryRecursively(path string, excludedPaths []string) *[]string {
return &directories
}

// FileCopy copy the specified file from src to dst path, and eventually encode its content to base64
func FileCopy(src, dst string, base64Encode bool) {
// FileCopy copy the specified file from src to dst path, and eventually encode its content to base64. Return copied file path
func FileCopy(src, dst string, base64Encode bool) string {
dst += fmt.Sprintf("%d_%s.fastfinder", time.Now().Unix(), filepath.Base(src))
srcFile, err := os.Open(src)
if err != nil {
Expand All @@ -215,6 +215,8 @@ func FileCopy(src, dst string, base64Encode bool) {
if err != nil {
LogFatal(fmt.Sprintf("%v", err))
}

return dst
}

// Contains checks if a string is contained in a slice of strings
Expand Down
Loading

0 comments on commit 3674dd0

Please sign in to comment.