Compare commits

..

7 Commits

Author SHA1 Message Date
5e5d0494a9 refactor: Improve Go docs (#74)
* add go docs, package: parser
* add go docs, package: protocols
* add go docs, package: tracer
2023-10-15 20:54:53 +02:00
d77aa0c8a0 Bump golang.org/x/net from 0.15.0 to 0.17.0 (#73) 2023-10-12 08:35:19 +02:00
3733c902b9 add Go Reference 2023-10-11 23:35:58 +02:00
07ffdd839f Refactoring, improve code coverage (#72)
* Refactoring, improve code coverage

* Add unit test for gelAllFilesNameByDirName

* Add codecov coverage into README.md

* Improve coverage readFileBytesByFilePath
2023-10-09 01:16:53 +02:00
1f48f4dff5 Feature: CI, Add codecov 2023-10-08 19:26:41 +02:00
88a96a7efd Update README.md
Add goreportcard

Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-10-08 17:47:33 +02:00
34a80b06f3 Feature: Refactor import for release v3 (#71)
Refactor import for release v3
2023-10-08 17:45:31 +02:00
19 changed files with 280 additions and 110 deletions

View File

@ -51,6 +51,11 @@ jobs:
exit 1 exit 1
fi fi
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Start integration test dependencies - name: Start integration test dependencies
run: | run: |
make test.dependencies.start make test.dependencies.start

View File

@ -1,6 +1,10 @@
# Beelzebub # Beelzebub
[![CI](https://github.com/mariocandela/beelzebub/actions/workflows/ci.yml/badge.svg)](https://github.com/mariocandela/beelzebub/actions/workflows/ci.yml) [![Docker](https://github.com/mariocandela/beelzebub/actions/workflows/docker-image.yml/badge.svg)](https://github.com/mariocandela/beelzebub/actions/workflows/docker-image.yml) [![codeql](https://github.com/mariocandela/beelzebub/actions/workflows/codeql.yml/badge.svg)](https://github.com/mariocandela/beelzebub/actions/workflows/codeql.yml) [![CI](https://github.com/mariocandela/beelzebub/actions/workflows/ci.yml/badge.svg)](https://github.com/mariocandela/beelzebub/actions/workflows/ci.yml) [![Docker](https://github.com/mariocandela/beelzebub/actions/workflows/docker-image.yml/badge.svg)](https://github.com/mariocandela/beelzebub/actions/workflows/docker-image.yml) [![codeql](https://github.com/mariocandela/beelzebub/actions/workflows/codeql.yml/badge.svg)](https://github.com/mariocandela/beelzebub/actions/workflows/codeql.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/mariocandela/beelzebub/v3)](https://goreportcard.com/report/github.com/mariocandela/beelzebub/v3)
[![codecov](https://codecov.io/gh/mariocandela/beelzebub/graph/badge.svg?token=8XTK7D4WHE)](https://codecov.io/gh/mariocandela/beelzebub)
[![Go Reference](https://pkg.go.dev/badge/github.com/mariocandela/beelzebub/v3.svg)](https://pkg.go.dev/github.com/mariocandela/beelzebub/v3)
## Overview ## Overview
@ -90,7 +94,9 @@ $ make test.unit
To run integration tests: To run integration tests:
```bash ```bash
$ make test.dependencies.start
$ make test.integration $ make test.integration
$ make test.dependencies.down
``` ```
## Key Features ## Key Features

View File

@ -3,10 +3,10 @@ package builder
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/mariocandela/beelzebub/parser" "github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/protocols" "github.com/mariocandela/beelzebub/v3/protocols"
"github.com/mariocandela/beelzebub/protocols/strategies" "github.com/mariocandela/beelzebub/v3/protocols/strategies"
"github.com/mariocandela/beelzebub/tracer" "github.com/mariocandela/beelzebub/v3/tracer"
"io" "io"
"net/http" "net/http"
"os" "os"

View File

@ -3,8 +3,8 @@ package builder
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/mariocandela/beelzebub/parser" "github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/tracer" "github.com/mariocandela/beelzebub/v3/tracer"
amqp "github.com/rabbitmq/amqp091-go" amqp "github.com/rabbitmq/amqp091-go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"

4
go.mod
View File

@ -1,4 +1,4 @@
module github.com/mariocandela/beelzebub module github.com/mariocandela/beelzebub/v3
go 1.20 go 1.20
@ -30,7 +30,7 @@ require (
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect github.com/prometheus/procfs v0.11.1 // indirect
golang.org/x/net v0.15.0 // indirect golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/protobuf v1.31.0 // indirect

3
go.sum
View File

@ -79,8 +79,9 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@ -2,9 +2,9 @@ package integration
import ( import (
"encoding/json" "encoding/json"
"github.com/mariocandela/beelzebub/builder" "github.com/mariocandela/beelzebub/v3/builder"
"github.com/mariocandela/beelzebub/parser" "github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/tracer" "github.com/mariocandela/beelzebub/v3/tracer"
"net" "net"
"net/http" "net/http"
"os" "os"

View File

@ -2,8 +2,8 @@ package main
import ( import (
"flag" "flag"
"github.com/mariocandela/beelzebub/builder" "github.com/mariocandela/beelzebub/v3/builder"
"github.com/mariocandela/beelzebub/parser" "github.com/mariocandela/beelzebub/v3/parser"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )

View File

@ -1,8 +1,8 @@
// Package parser is responsible for parsing the configurations of the core and honeypot service
package parser package parser
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -11,6 +11,7 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// BeelzebubCoreConfigurations is the struct that contains the configurations of the core
type BeelzebubCoreConfigurations struct { type BeelzebubCoreConfigurations struct {
Core struct { Core struct {
Logging Logging `yaml:"logging"` Logging Logging `yaml:"logging"`
@ -19,6 +20,7 @@ type BeelzebubCoreConfigurations struct {
} }
} }
// Logging is the struct that contains the configurations of the logging
type Logging struct { type Logging struct {
Debug bool `yaml:"debug"` Debug bool `yaml:"debug"`
DebugReportCaller bool `yaml:"debugReportCaller"` DebugReportCaller bool `yaml:"debugReportCaller"`
@ -26,6 +28,7 @@ type Logging struct {
LogsPath string `yaml:"logsPath,omitempty"` LogsPath string `yaml:"logsPath,omitempty"`
} }
// Tracings is the struct that contains the configurations of the tracings
type Tracings struct { type Tracings struct {
RabbitMQ `yaml:"rabbit-mq"` RabbitMQ `yaml:"rabbit-mq"`
} }
@ -43,6 +46,7 @@ type Plugin struct {
OpenAPIChatGPTSecretKey string `yaml:"openAPIChatGPTSecretKey"` OpenAPIChatGPTSecretKey string `yaml:"openAPIChatGPTSecretKey"`
} }
// BeelzebubServiceConfiguration is the struct that contains the configurations of the honeypot service
type BeelzebubServiceConfiguration struct { type BeelzebubServiceConfiguration struct {
ApiVersion string `yaml:"apiVersion"` ApiVersion string `yaml:"apiVersion"`
Protocol string `yaml:"protocol"` Protocol string `yaml:"protocol"`
@ -57,6 +61,7 @@ type BeelzebubServiceConfiguration struct {
Plugin Plugin `yaml:"plugin"` Plugin Plugin `yaml:"plugin"`
} }
// Command is the struct that contains the configurations of the commands
type Command struct { type Command struct {
Regex string `yaml:"regex"` Regex string `yaml:"regex"`
Handler string `yaml:"handler"` Handler string `yaml:"handler"`
@ -86,6 +91,7 @@ func Init(configurationsCorePath, configurationsServicesDirectory string) *confi
} }
} }
// ReadConfigurationsCore is the method that reads the configurations of the core from files
func (bp configurationsParser) ReadConfigurationsCore() (*BeelzebubCoreConfigurations, error) { func (bp configurationsParser) ReadConfigurationsCore() (*BeelzebubCoreConfigurations, error) {
buf, err := bp.readFileBytesByFilePathDependency(bp.configurationsCorePath) buf, err := bp.readFileBytesByFilePathDependency(bp.configurationsCorePath)
if err != nil { if err != nil {
@ -101,6 +107,7 @@ func (bp configurationsParser) ReadConfigurationsCore() (*BeelzebubCoreConfigura
return beelzebubConfiguration, nil return beelzebubConfiguration, nil
} }
// ReadConfigurationsServices is the method that reads the configurations of the honeypot services from files
func (bp configurationsParser) ReadConfigurationsServices() ([]BeelzebubServiceConfiguration, error) { func (bp configurationsParser) ReadConfigurationsServices() ([]BeelzebubServiceConfiguration, error) {
services, err := bp.gelAllFilesNameByDirNameDependency(bp.configurationsServicesDirectory) services, err := bp.gelAllFilesNameByDirNameDependency(bp.configurationsServicesDirectory)
if err != nil { if err != nil {
@ -127,7 +134,7 @@ func (bp configurationsParser) ReadConfigurationsServices() ([]BeelzebubServiceC
} }
func gelAllFilesNameByDirName(dirName string) ([]string, error) { func gelAllFilesNameByDirName(dirName string) ([]string, error) {
files, err := ioutil.ReadDir(dirName) files, err := os.ReadDir(dirName)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,6 +2,7 @@ package parser
import ( import (
"errors" "errors"
"os"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -118,3 +119,54 @@ func TestReadConfigurationsServicesValid(t *testing.T) {
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Commands[0].Headers), 1) assert.Equal(t, len(firstBeelzebubServiceConfiguration.Commands[0].Headers), 1)
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Headers[0], "Content-Type: text/html") assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Headers[0], "Content-Type: text/html")
} }
func TestGelAllFilesNameByDirName(t *testing.T) {
var dir = t.TempDir()
files, err := gelAllFilesNameByDirName(dir)
assert.Nil(t, err)
assert.Equal(t, 0, len(files))
}
func TestGelAllFilesNameByDirNameFiles(t *testing.T) {
var dir = t.TempDir()
testFiles := []string{"file1.yaml", "file2.yaml", "file3.txt", "subdir", "file4.yaml"}
for _, filename := range testFiles {
filePath := dir + "/" + filename
file, err := os.Create(filePath)
assert.NoError(t, err)
file.Close()
}
files, err := gelAllFilesNameByDirName(dir)
assert.Nil(t, err)
assert.Equal(t, 3, len(files))
}
func TestGelAllFilesNameByDirNameError(t *testing.T) {
files, err := gelAllFilesNameByDirName("nosuchfile")
assert.Nil(t, files)
assert.Equal(t, "open nosuchfile: no such file or directory", err.Error())
}
func TestReadFileBytesByFilePath(t *testing.T) {
var dir = t.TempDir()
filePath := dir + "/test.yaml"
f, err := os.Create(filePath)
assert.NoError(t, err)
f.Close()
bytes, err := readFileBytesByFilePath(filePath)
assert.NoError(t, err)
assert.Equal(t, "", string(bytes))
}

View File

@ -5,33 +5,27 @@ import (
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
const ( const (
// Reference: https://www.engraved.blog/building-a-virtual-machine-inside/ // Reference: https://www.engraved.blog/building-a-virtual-machine-inside/
promptVirtualizeLinuxTerminal = "I want you to act as a Linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. Do no write explanations. Do not type commands unless I instruct you to do so.\n\nA:pwd\n\nQ:/home/user\n\n" promptVirtualizeLinuxTerminal = "I want you to act as a Linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. Do no write explanations. Do not type commands unless I instruct you to do so.\n\nA:pwd\n\nQ:/home/user\n\n"
ChatGPTPluginName = "OpenAIGPTLinuxTerminal" ChatGPTPluginName = "OpenAIGPTLinuxTerminal"
openAIGPTEndpoint = "https://api.openai.com/v1/completions" openAIGPTEndpoint = "https://api.openai.com/v1/completions"
) )
type History struct { type History struct {
Input, Output string Input, Output string
} }
type OpenAIGPTVirtualTerminal struct {
Histories []History
OpenAPIChatGPTSecretKey string
client *resty.Client
}
func (openAIGPTVirtualTerminal *OpenAIGPTVirtualTerminal) InjectDependency() { type openAIGPTVirtualTerminal struct {
if openAIGPTVirtualTerminal.client == nil { Histories []History
openAIGPTVirtualTerminal.client = resty.New() openAIKey string
} client *resty.Client
} }
type Choice struct { type Choice struct {
@ -65,6 +59,14 @@ type gptRequest struct {
Stop []string `json:"stop"` Stop []string `json:"stop"`
} }
func Init(history []History, openAIKey string) *openAIGPTVirtualTerminal {
return &openAIGPTVirtualTerminal{
Histories: history,
openAIKey: openAIKey,
client: resty.New(),
}
}
func buildPrompt(histories []History, command string) string { func buildPrompt(histories []History, command string) string {
var sb strings.Builder var sb strings.Builder
@ -79,7 +81,7 @@ func buildPrompt(histories []History, command string) string {
return sb.String() return sb.String()
} }
func (openAIGPTVirtualTerminal *OpenAIGPTVirtualTerminal) GetCompletions(command string) (string, error) { func (openAIGPTVirtualTerminal *openAIGPTVirtualTerminal) GetCompletions(command string) (string, error) {
requestJson, err := json.Marshal(gptRequest{ requestJson, err := json.Marshal(gptRequest{
Model: "text-davinci-003", Model: "text-davinci-003",
Prompt: buildPrompt(openAIGPTVirtualTerminal.Histories, command), Prompt: buildPrompt(openAIGPTVirtualTerminal.Histories, command),
@ -94,14 +96,14 @@ func (openAIGPTVirtualTerminal *OpenAIGPTVirtualTerminal) GetCompletions(command
return "", err return "", err
} }
if openAIGPTVirtualTerminal.OpenAPIChatGPTSecretKey == "" { if openAIGPTVirtualTerminal.openAIKey == "" {
return "", errors.New("OpenAPIChatGPTSecretKey is empty") return "", errors.New("openAIKey is empty")
} }
response, err := openAIGPTVirtualTerminal.client.R(). response, err := openAIGPTVirtualTerminal.client.R().
SetHeader("Content-Type", "application/json"). SetHeader("Content-Type", "application/json").
SetBody(requestJson). SetBody(requestJson).
SetAuthToken(openAIGPTVirtualTerminal.OpenAPIChatGPTSecretKey). SetAuthToken(openAIGPTVirtualTerminal.openAIKey).
SetResult(&gptResponse{}). SetResult(&gptResponse{}).
Post(openAIGPTEndpoint) Post(openAIGPTEndpoint)

View File

@ -46,7 +46,15 @@ func TestBuildPromptWithHistory(t *testing.T) {
prompt) prompt)
} }
func TestBuildGetCompletions(t *testing.T) { func TestBuildGetCompletionsFailValidation(t *testing.T) {
openAIGPTVirtualTerminal := Init(make([]History, 0), "")
_, err := openAIGPTVirtualTerminal.GetCompletions("test")
assert.Equal(t, "openAIKey is empty", err.Error())
}
func TestBuildGetCompletionsWithResults(t *testing.T) {
client := resty.New() client := resty.New()
httpmock.ActivateNonDefault(client.GetClient()) httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset() defer httpmock.DeactivateAndReset()
@ -68,10 +76,8 @@ func TestBuildGetCompletions(t *testing.T) {
}, },
) )
openAIGPTVirtualTerminal := OpenAIGPTVirtualTerminal{ openAIGPTVirtualTerminal := Init(make([]History, 0), "sdjdnklfjndslkjanfk")
OpenAPIChatGPTSecretKey: "sdjdnklfjndslkjanfk", openAIGPTVirtualTerminal.client = client
client: client,
}
//When //When
str, err := openAIGPTVirtualTerminal.GetCompletions("ls") str, err := openAIGPTVirtualTerminal.GetCompletions("ls")
@ -80,3 +86,31 @@ func TestBuildGetCompletions(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "prova.txt", str) assert.Equal(t, "prova.txt", str)
} }
func TestBuildGetCompletionsWithoutResults(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &gptResponse{
Choices: []Choice{},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
openAIGPTVirtualTerminal := Init(make([]History, 0), "sdjdnklfjndslkjanfk")
openAIGPTVirtualTerminal.client = client
//When
_, err := openAIGPTVirtualTerminal.GetCompletions("ls")
//Then
assert.Equal(t, "no choices", err.Error())
}

View File

@ -1,10 +1,12 @@
// Package protocols is responsible for managing the different protocols
package protocols package protocols
import ( import (
"github.com/mariocandela/beelzebub/parser" "github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/tracer" "github.com/mariocandela/beelzebub/v3/tracer"
) )
// ServiceStrategy is the common interface that each protocol honeypot implements
type ServiceStrategy interface { type ServiceStrategy interface {
Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tracer tracer.Tracer) error Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tracer tracer.Tracer) error
} }
@ -14,10 +16,11 @@ type ProtocolManager struct {
tracer tracer.Tracer tracer tracer.Tracer
} }
func InitProtocolManager(tracerStrategy tracer.Strategy, strategy ServiceStrategy) *ProtocolManager { // InitProtocolManager is the method that initializes the protocol manager, receving the concrete tracer and the concrete service
func InitProtocolManager(tracerStrategy tracer.Strategy, serviceStrategy ServiceStrategy) *ProtocolManager {
return &ProtocolManager{ return &ProtocolManager{
tracer: tracer.Init(tracerStrategy), tracer: tracer.GetInstance(tracerStrategy),
strategy: strategy, strategy: serviceStrategy,
} }
} }
@ -25,6 +28,7 @@ func (pm *ProtocolManager) SetProtocolStrategy(strategy ServiceStrategy) {
pm.strategy = strategy pm.strategy = strategy
} }
// InitService is the method that initializes the honeypot
func (pm *ProtocolManager) InitService(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration) error { func (pm *ProtocolManager) InitService(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration) error {
return pm.strategy.Init(beelzebubServiceConfiguration, pm.tracer) return pm.strategy.Init(beelzebubServiceConfiguration, pm.tracer)
} }

View File

@ -2,8 +2,8 @@ package protocols
import ( import (
"errors" "errors"
"github.com/mariocandela/beelzebub/parser" "github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/tracer" "github.com/mariocandela/beelzebub/v3/tracer"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"testing" "testing"
) )

View File

@ -2,8 +2,8 @@ package strategies
import ( import (
"fmt" "fmt"
"github.com/mariocandela/beelzebub/parser" "github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/tracer" "github.com/mariocandela/beelzebub/v3/tracer"
"io" "io"
"net/http" "net/http"
"regexp" "regexp"

View File

@ -2,10 +2,11 @@ package strategies
import ( import (
"fmt" "fmt"
"github.com/mariocandela/beelzebub/parser" "github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/plugins" "github.com/mariocandela/beelzebub/v3/plugins"
"github.com/mariocandela/beelzebub/tracer" "github.com/mariocandela/beelzebub/v3/tracer"
"regexp" "regexp"
"strings" "strings"
"time" "time"
@ -62,8 +63,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
commandOutput := command.Handler commandOutput := command.Handler
if command.Plugin == plugins.ChatGPTPluginName { if command.Plugin == plugins.ChatGPTPluginName {
openAIGPTVirtualTerminal := plugins.OpenAIGPTVirtualTerminal{Histories: histories, OpenAPIChatGPTSecretKey: beelzebubServiceConfiguration.Plugin.OpenAPIChatGPTSecretKey} openAIGPTVirtualTerminal := plugins.Init(histories, beelzebubServiceConfiguration.Plugin.OpenAPIChatGPTSecretKey)
openAIGPTVirtualTerminal.InjectDependency()
if commandOutput, err = openAIGPTVirtualTerminal.GetCompletions(commandInput); err != nil { if commandOutput, err = openAIGPTVirtualTerminal.GetCompletions(commandInput); err != nil {
log.Errorf("Error GetCompletions: %s, %s", commandInput, err.Error()) log.Errorf("Error GetCompletions: %s, %s", commandInput, err.Error())
@ -124,7 +124,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"port": beelzebubServiceConfiguration.Address, "port": beelzebubServiceConfiguration.Address,
"commands": len(beelzebubServiceConfiguration.Commands), "commands": len(beelzebubServiceConfiguration.Commands),
}).Infof("Init service %s", beelzebubServiceConfiguration.Protocol) }).Infof("GetInstance service %s", beelzebubServiceConfiguration.Protocol)
return nil return nil
} }

View File

@ -2,8 +2,8 @@ package strategies
import ( import (
"fmt" "fmt"
"github.com/mariocandela/beelzebub/parser" "github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/tracer" "github.com/mariocandela/beelzebub/v3/tracer"
"net" "net"
"time" "time"

View File

@ -1,13 +1,16 @@
// Package tracer is responsible for tracing the events that occur in the honeypots
package tracer package tracer
import ( import (
log "github.com/sirupsen/logrus"
"sync"
"time" "time"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
log "github.com/sirupsen/logrus"
) )
// Workers is the number of workers that will
const Workers = 5 const Workers = 5
type Event struct { type Event struct {
@ -44,8 +47,8 @@ const (
TCP TCP
) )
func (status Protocol) String() string { func (protocol Protocol) String() string {
return [...]string{"HTTP", "SSH", "TCP"}[status] return [...]string{"HTTP", "SSH", "TCP"}[protocol]
} }
const ( const (
@ -66,49 +69,60 @@ type Tracer interface {
} }
type tracer struct { type tracer struct {
strategy Strategy strategy Strategy
eventsChan chan Event eventsChan chan Event
eventsTotal prometheus.Counter
eventsSSHTotal prometheus.Counter
eventsTCPTotal prometheus.Counter
eventsHTTPTotal prometheus.Counter
} }
var ( var lock = &sync.Mutex{}
eventsTotal = promauto.NewCounter(prometheus.CounterOpts{ var singleton *tracer
Namespace: "beelzebub",
Name: "events_total",
Help: "The total number of events",
})
eventsSSHTotal = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "ssh_events_total",
Help: "The total number of SSH events",
})
eventsTCPTotal = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "tcp_events_total",
Help: "The total number of TCP events",
})
eventsHTTPTotal = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "http_events_total",
Help: "The total number of HTTP events",
})
)
func Init(strategy Strategy) *tracer { func GetInstance(defaultStrategy Strategy) *tracer {
tracer := &tracer{ if singleton == nil {
strategy: strategy, lock.Lock()
eventsChan: make(chan Event, Workers), defer lock.Unlock()
} // This is to prevent expensive lock operations every time the GetInstance method is called
if singleton == nil {
for i := 0; i < Workers; i++ { singleton = &tracer{
go func(i int) { strategy: defaultStrategy,
log.Debug("Init trace worker: ", i) eventsChan: make(chan Event, Workers),
for event := range tracer.eventsChan { eventsTotal: promauto.NewCounter(prometheus.CounterOpts{
tracer.strategy(event) Namespace: "beelzebub",
Name: "events_total",
Help: "The total number of events",
}),
eventsSSHTotal: promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "ssh_events_total",
Help: "The total number of SSH events",
}),
eventsTCPTotal: promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "tcp_events_total",
Help: "The total number of TCP events",
}),
eventsHTTPTotal: promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "http_events_total",
Help: "The total number of HTTP events",
}),
} }
}(i)
for i := 0; i < Workers; i++ {
go func(i int) {
log.Debug("Trace worker: ", i)
for event := range singleton.eventsChan {
singleton.strategy(event)
}
}(i)
}
}
} }
return tracer return singleton
} }
func (tracer *tracer) setStrategy(strategy Strategy) { func (tracer *tracer) setStrategy(strategy Strategy) {
@ -120,14 +134,17 @@ func (tracer *tracer) TraceEvent(event Event) {
tracer.eventsChan <- event tracer.eventsChan <- event
eventsTotal.Inc() tracer.updatePrometheusCounters(event.Protocol)
}
switch event.Protocol {
case HTTP.String(): func (tracer *tracer) updatePrometheusCounters(protocol string) {
eventsHTTPTotal.Inc() switch protocol {
case SSH.String(): case HTTP.String():
eventsSSHTotal.Inc() tracer.eventsHTTPTotal.Inc()
case TCP.String(): case SSH.String():
eventsTCPTotal.Inc() tracer.eventsSSHTotal.Inc()
} case TCP.String():
tracer.eventsTCPTotal.Inc()
}
tracer.eventsTotal.Inc()
} }

View File

@ -1,6 +1,7 @@
package tracer package tracer
import ( import (
"github.com/prometheus/client_golang/prometheus"
"sync" "sync"
"testing" "testing"
@ -10,7 +11,7 @@ import (
func TestInit(t *testing.T) { func TestInit(t *testing.T) {
mockStrategy := func(event Event) {} mockStrategy := func(event Event) {}
tracer := Init(mockStrategy) tracer := GetInstance(mockStrategy)
assert.NotNil(t, tracer.strategy) assert.NotNil(t, tracer.strategy)
} }
@ -25,7 +26,9 @@ func TestTraceEvent(t *testing.T) {
eventCalled = event eventCalled = event
} }
tracer := Init(mockStrategy) tracer := GetInstance(mockStrategy)
tracer.strategy = mockStrategy
wg.Add(1) wg.Add(1)
tracer.TraceEvent(Event{ tracer.TraceEvent(Event{
@ -51,7 +54,7 @@ func TestSetStrategy(t *testing.T) {
eventCalled = event eventCalled = event
} }
tracer := Init(mockStrategy) tracer := GetInstance(mockStrategy)
tracer.setStrategy(mockStrategy) tracer.setStrategy(mockStrategy)
@ -75,3 +78,42 @@ func TestStringStatus(t *testing.T) {
assert.Equal(t, Stateless.String(), "Stateless") assert.Equal(t, Stateless.String(), "Stateless")
assert.Equal(t, Interaction.String(), "Interaction") assert.Equal(t, Interaction.String(), "Interaction")
} }
type mockCounter struct {
prometheus.Metric
prometheus.Collector
inc func()
add func(float64)
}
var counter = 0
func (m mockCounter) Inc() {
counter += 1
}
func (m mockCounter) Add(f float64) {
counter = int(f)
}
func TestUpdatePrometheusCounters(t *testing.T) {
mockStrategy := func(event Event) {}
tracer := &tracer{
strategy: mockStrategy,
eventsChan: make(chan Event, Workers),
eventsTotal: mockCounter{},
eventsSSHTotal: mockCounter{},
eventsTCPTotal: mockCounter{},
eventsHTTPTotal: mockCounter{},
}
tracer.updatePrometheusCounters(SSH.String())
assert.Equal(t, 2, counter)
tracer.updatePrometheusCounters(HTTP.String())
assert.Equal(t, 4, counter)
tracer.updatePrometheusCounters(TCP.String())
assert.Equal(t, 6, counter)
}