mirror of
https://github.com/mariocandela/beelzebub.git
synced 2025-07-01 18:47:26 -04:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
e4ca84589b | |||
31f5ca9cb1 | |||
c6855e8511 | |||
6d2500d0f5 | |||
c98b888985 | |||
d062435818 | |||
9ddb076621 | |||
5bfdff7097 | |||
fa7d17f817 | |||
fafa2bcb05 | |||
3801628ef1 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "Quality Gate: checking test coverage is above threshold ..."
|
echo "Quality Gate: checking test coverage is above threshold ..."
|
||||||
echo "Threshold : $TESTCOVERAGE_THRESHOLD %"
|
echo "Threshold : $TESTCOVERAGE_THRESHOLD %"
|
||||||
# Excluded the concrete strategy
|
# Excluded the concrete strategy from the coverage calculation, this will be tested in the integration tests
|
||||||
cat coverage.tmp.out | grep -v "secureShellStrategy.go" | grep -v "hypertextTransferProtocolStrategy.go" | grep -v "transmissionControlProtocolStrategy.go" > coverage.out
|
cat coverage.tmp.out | grep -v "secureShellStrategy.go" | grep -v "hypertextTransferProtocolStrategy.go" | grep -v "transmissionControlProtocolStrategy.go" > coverage.out
|
||||||
totalCoverage=`go tool cover -func=coverage.out | grep total | grep -Eo '[0-9]+\.[0-9]+'`
|
totalCoverage=`go tool cover -func=coverage.out | grep total | grep -Eo '[0-9]+\.[0-9]+'`
|
||||||
echo "Current test coverage : $totalCoverage %"
|
echo "Current test coverage : $totalCoverage %"
|
||||||
|
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
mario.candela.personal@gmail.com.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
@ -29,6 +29,8 @@ RUN cp /build/main .
|
|||||||
# Build a small image
|
# Build a small image
|
||||||
FROM scratch
|
FROM scratch
|
||||||
|
|
||||||
|
# copy the ca-certificate.crt from the builder stage
|
||||||
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
COPY --from=builder /dist/main /
|
COPY --from=builder /dist/main /
|
||||||
|
|
||||||
ENTRYPOINT ["/main"]
|
ENTRYPOINT ["/main"]
|
43
README.md
43
README.md
@ -31,6 +31,18 @@ Unit Test:
|
|||||||
$ go test ./...
|
$ go test ./...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- OpenAPI ChatBot GPT-3 integration
|
||||||
|
- SSH Honeypot
|
||||||
|
- HTTP Honeypot
|
||||||
|
- TCP Honeypot
|
||||||
|
- Easy to create a new strategy
|
||||||
|
- Easy to extend event tracking logic
|
||||||
|
- Strong code quality
|
||||||
|
- Docker
|
||||||
|
- RabbitMQ integration
|
||||||
|
|
||||||
## Example configuration service
|
## Example configuration service
|
||||||
|
|
||||||
The configurations are inside the /configurations/services directory, just add a new file for each service/port.
|
The configurations are inside the /configurations/services directory, just add a new file for each service/port.
|
||||||
@ -86,6 +98,24 @@ commands:
|
|||||||
|
|
||||||
### Example SSH Honeypot
|
### Example SSH Honeypot
|
||||||
|
|
||||||
|
###### Honeypot with ChatBot GPT-3 ssh-2222.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: "v1"
|
||||||
|
protocol: "ssh"
|
||||||
|
address: ":2222"
|
||||||
|
description: "SSH interactive ChatGPT"
|
||||||
|
commands:
|
||||||
|
- regex: "^(.+)$"
|
||||||
|
plugin: "OpenAIGPTLinuxTerminal"
|
||||||
|
serverVersion: "OpenSSH"
|
||||||
|
serverName: "ubuntu"
|
||||||
|
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
|
||||||
|
deadlineTimeoutSeconds: 60
|
||||||
|
plugin:
|
||||||
|
openAPIChatGPTSecretKey: "Here your ChatBot SecretKey "
|
||||||
|
```
|
||||||
|
|
||||||
###### ssh-22.yaml
|
###### ssh-22.yaml
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@ -118,17 +148,6 @@ deadlineTimeoutSeconds: 60
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- SSH Honeypot
|
|
||||||
- HTTP Honeypot
|
|
||||||
- TCP Honeypot
|
|
||||||
- Easy to create a new strategy
|
|
||||||
- Easy to extend event tracking logic
|
|
||||||
- Strong code quality
|
|
||||||
- Docker
|
|
||||||
- RabbitMQ integration
|
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- telnet
|
- telnet
|
||||||
@ -154,3 +173,5 @@ Happy hacking!
|
|||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under [GNU GPL 3 License](LICENSE).
|
This project is licensed under [GNU GPL 3 License](LICENSE).
|
||||||
|
|
||||||
|
[](https://www.paypal.com/donate/?business=P75FH5LXKQTAC&no_recurring=0¤cy_code=EUR)
|
||||||
|
125
builder/Builder.go
Normal file
125
builder/Builder.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"beelzebub/parser"
|
||||||
|
"beelzebub/protocols"
|
||||||
|
"beelzebub/tracer"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
amqp "github.com/rabbitmq/amqp091-go"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const RabbitmqQueueName = "event"
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
beelzebubServicesConfiguration []parser.BeelzebubServiceConfiguration
|
||||||
|
traceStrategy tracer.Strategy
|
||||||
|
rabbitMQChannel *amqp.Channel
|
||||||
|
rabbitMQConnection *amqp.Connection
|
||||||
|
logsFile *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) setTraceStrategy(traceStrategy tracer.Strategy) {
|
||||||
|
b.traceStrategy = traceStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) buildLogger(configurations parser.Logging) error {
|
||||||
|
logsFile, err := os.OpenFile(configurations.LogsPath, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetOutput(io.MultiWriter(os.Stdout, logsFile))
|
||||||
|
|
||||||
|
log.SetFormatter(&log.JSONFormatter{
|
||||||
|
DisableTimestamp: configurations.LogDisableTimestamp,
|
||||||
|
})
|
||||||
|
log.SetReportCaller(configurations.DebugReportCaller)
|
||||||
|
if configurations.Debug {
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
} else {
|
||||||
|
log.SetLevel(log.InfoLevel)
|
||||||
|
}
|
||||||
|
b.logsFile = logsFile
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) buildRabbitMQ(rabbitMQURI string) error {
|
||||||
|
rabbitMQConnection, err := amqp.Dial(rabbitMQURI)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.rabbitMQChannel, err = rabbitMQConnection.Channel()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//creates a queue if it doesn't already exist, or ensures that an existing queue matches the same parameters.
|
||||||
|
if _, err = b.rabbitMQChannel.QueueDeclare(RabbitmqQueueName, false, false, false, false, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.rabbitMQConnection = rabbitMQConnection
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Close() error {
|
||||||
|
if err := b.rabbitMQChannel.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := b.rabbitMQConnection.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := b.logsFile.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Run() error {
|
||||||
|
// Init Protocol strategies
|
||||||
|
secureShellStrategy := &protocols.SecureShellStrategy{}
|
||||||
|
hypertextTransferProtocolStrategy := &protocols.HypertextTransferProtocolStrategy{}
|
||||||
|
transmissionControlProtocolStrategy := &protocols.TransmissionControlProtocolStrategy{}
|
||||||
|
|
||||||
|
// Init Tracer strategies, and set the trace strategy default HTTP
|
||||||
|
protocolManager := protocols.InitProtocolManager(b.traceStrategy, hypertextTransferProtocolStrategy)
|
||||||
|
|
||||||
|
for _, beelzebubServiceConfiguration := range b.beelzebubServicesConfiguration {
|
||||||
|
switch beelzebubServiceConfiguration.Protocol {
|
||||||
|
case "http":
|
||||||
|
protocolManager.SetProtocolStrategy(hypertextTransferProtocolStrategy)
|
||||||
|
break
|
||||||
|
case "ssh":
|
||||||
|
protocolManager.SetProtocolStrategy(secureShellStrategy)
|
||||||
|
break
|
||||||
|
case "tcp":
|
||||||
|
protocolManager.SetProtocolStrategy(transmissionControlProtocolStrategy)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
log.Fatalf("Protocol %s not managed", beelzebubServiceConfiguration.Protocol)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := protocolManager.InitService(beelzebubServiceConfiguration); err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("Error during init protocol: %s, %s", beelzebubServiceConfiguration.Protocol, err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) build() *Builder {
|
||||||
|
return &Builder{
|
||||||
|
beelzebubServicesConfiguration: b.beelzebubServicesConfiguration,
|
||||||
|
traceStrategy: b.traceStrategy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBuilder() *Builder {
|
||||||
|
return &Builder{}
|
||||||
|
}
|
67
builder/Director.go
Normal file
67
builder/Director.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"beelzebub/parser"
|
||||||
|
"beelzebub/tracer"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
amqp "github.com/rabbitmq/amqp091-go"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Director struct {
|
||||||
|
builder *Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDirector(builder *Builder) *Director {
|
||||||
|
return &Director{
|
||||||
|
builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Director) BuildBeelzebub(beelzebubCoreConfigurations *parser.BeelzebubCoreConfigurations, beelzebubServicesConfiguration []parser.BeelzebubServiceConfiguration) (*Builder, error) {
|
||||||
|
d.builder.beelzebubServicesConfiguration = beelzebubServicesConfiguration
|
||||||
|
|
||||||
|
if err := d.builder.buildLogger(beelzebubCoreConfigurations.Core.Logging); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.builder.setTraceStrategy(d.standardOutStrategy)
|
||||||
|
|
||||||
|
if beelzebubCoreConfigurations.Core.Tracing.RabbitMQEnabled {
|
||||||
|
d.builder.setTraceStrategy(d.rabbitMQTraceStrategy)
|
||||||
|
err := d.builder.buildRabbitMQ(beelzebubCoreConfigurations.Core.Tracing.RabbitMQURI)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.builder.build(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Director) standardOutStrategy(event tracer.Event) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"status": event.Status,
|
||||||
|
"event": event,
|
||||||
|
}).Info("New Event")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Director) rabbitMQTraceStrategy(event tracer.Event) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"status": event.Status,
|
||||||
|
"event": event,
|
||||||
|
}).Info("New Event")
|
||||||
|
|
||||||
|
log.Debug("Push Event on queue")
|
||||||
|
eventJSON, err := json.Marshal(event)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing := amqp.Publishing{ContentType: "application/json", Body: eventJSON}
|
||||||
|
|
||||||
|
if err = d.builder.rabbitMQChannel.PublishWithContext(context.TODO(), "", RabbitmqQueueName, false, false, publishing); err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
13
configurations/services/ssh-2222.yaml
Normal file
13
configurations/services/ssh-2222.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: "v1"
|
||||||
|
protocol: "ssh"
|
||||||
|
address: ":2222"
|
||||||
|
description: "SSH interactive ChatGPT"
|
||||||
|
commands:
|
||||||
|
- regex: "^(.+)$"
|
||||||
|
plugin: "OpenAIGPTLinuxTerminal"
|
||||||
|
serverVersion: "OpenSSH"
|
||||||
|
serverName: "ubuntu"
|
||||||
|
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
|
||||||
|
deadlineTimeoutSeconds: 60
|
||||||
|
plugin:
|
||||||
|
openAPIChatGPTSecretKey: ""
|
@ -8,10 +8,11 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
ports: # Remove me, if you use configuration network_mode: host
|
ports: # Remove me, if you use configuration network_mode: host
|
||||||
- "22:22"
|
- "22:22"
|
||||||
|
- "2222:2222"
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "3306:3306"
|
- "3306:3306"
|
||||||
environment:
|
environment:
|
||||||
RABBITMQ_URI: ${RABBITMQ_URI}
|
RABBITMQ_URI: ${RABBITMQ_URI}
|
||||||
volumes:
|
volumes:
|
||||||
- "./configurations:/configurations"
|
- "./configurations:/configurations"
|
||||||
|
6
go.mod
6
go.mod
@ -4,10 +4,12 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gliderlabs/ssh v0.3.5
|
github.com/gliderlabs/ssh v0.3.5
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/rabbitmq/amqp091-go v1.4.0
|
github.com/jarcoal/httpmock v1.2.0
|
||||||
|
github.com/rabbitmq/amqp091-go v1.5.0
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.1
|
||||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
|
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
16
go.sum
16
go.sum
@ -5,25 +5,33 @@ 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc=
|
||||||
|
github.com/jarcoal/httpmock v1.2.0/go.mod h1:oCoTsnAz4+UoOUIf5lJOWV2QQIW5UoeUI6aM2YnWAZk=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/maxatome/go-testdeep v1.11.0 h1:Tgh5efyCYyJFGUYiT0qxBSIDeXw0F5zSoatlou685kk=
|
||||||
|
github.com/maxatome/go-testdeep v1.11.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rabbitmq/amqp091-go v1.4.0 h1:T2G+J9W9OY4p64Di23J6yH7tOkMocgnESvYeBjuG9cY=
|
github.com/rabbitmq/amqp091-go v1.5.0 h1:VouyHPBu1CrKyJVfteGknGOGCzmOz0zcv/tONLkb7rg=
|
||||||
github.com/rabbitmq/amqp091-go v1.4.0/go.mod h1:JsV0ofX5f1nwOGafb8L5rBItt9GyhfQfcJj+oyz0dGg=
|
github.com/rabbitmq/amqp091-go v1.5.0/go.mod h1:JsV0ofX5f1nwOGafb8L5rBItt9GyhfQfcJj+oyz0dGg=
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
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.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.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=
|
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=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
@ -37,7 +45,9 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||||
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/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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
114
main.go
114
main.go
@ -1,74 +1,38 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"beelzebub/builder"
|
||||||
"beelzebub/parser"
|
"beelzebub/parser"
|
||||||
"beelzebub/protocols"
|
|
||||||
"beelzebub/tracer"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
amqp "github.com/rabbitmq/amqp091-go"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var quit = make(chan struct{})
|
var quit = make(chan struct{})
|
||||||
|
|
||||||
var channel *amqp.Channel
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
parser := parser.Init("./configurations/beelzebub.yaml", "./configurations/services/")
|
parser := parser.Init("./configurations/beelzebub.yaml", "./configurations/services/")
|
||||||
|
|
||||||
coreConfigurations, err := parser.ReadConfigurationsCore()
|
coreConfigurations, err := parser.ReadConfigurationsCore()
|
||||||
failOnError(err, fmt.Sprintf("Error during ReadConfigurationsCore: "))
|
failOnError(err, fmt.Sprintf("Error during ReadConfigurationsCore: "))
|
||||||
|
|
||||||
fileLogs := configureLoggingByConfigurations(coreConfigurations.Core.Logging)
|
|
||||||
defer fileLogs.Close()
|
|
||||||
|
|
||||||
beelzebubServicesConfiguration, err := parser.ReadConfigurationsServices()
|
beelzebubServicesConfiguration, err := parser.ReadConfigurationsServices()
|
||||||
failOnError(err, fmt.Sprintf("Error during ReadConfigurationsServices: "))
|
failOnError(err, fmt.Sprintf("Error during ReadConfigurationsServices: "))
|
||||||
|
|
||||||
if coreConfigurations.Core.Tracing.RabbitMQEnabled {
|
beelzebubBuilder := builder.NewBuilder()
|
||||||
rabbitMQURI, configured := os.LookupEnv("RABBITMQ_URI")
|
|
||||||
if !configured {
|
|
||||||
rabbitMQURI = coreConfigurations.Core.Tracing.RabbitMQURI
|
|
||||||
}
|
|
||||||
conn, err := amqp.Dial(rabbitMQURI)
|
|
||||||
failOnError(err, "Failed to connect to RabbitMQ")
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
channel, err = conn.Channel()
|
director := builder.NewDirector(beelzebubBuilder)
|
||||||
failOnError(err, "Failed to open a channel")
|
|
||||||
defer channel.Close()
|
beelzebubBuilder, err = director.BuildBeelzebub(coreConfigurations, beelzebubServicesConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init Protocol strategies
|
if err := beelzebubBuilder.Run(); err != nil {
|
||||||
secureShellStrategy := &protocols.SecureShellStrategy{}
|
log.Fatal(err)
|
||||||
hypertextTransferProtocolStrategy := &protocols.HypertextTransferProtocolStrategy{}
|
return
|
||||||
transmissionControlProtocolStrategy := &protocols.TransmissionControlProtocolStrategy{}
|
|
||||||
|
|
||||||
// Init protocol manager, with simple log on stout trace strategy and default protocol HTTP
|
|
||||||
protocolManager := protocols.InitProtocolManager(traceStrategyStdoutAndRabbitMQ, hypertextTransferProtocolStrategy)
|
|
||||||
|
|
||||||
for _, beelzebubServiceConfiguration := range beelzebubServicesConfiguration {
|
|
||||||
switch beelzebubServiceConfiguration.Protocol {
|
|
||||||
case "http":
|
|
||||||
protocolManager.SetProtocolStrategy(hypertextTransferProtocolStrategy)
|
|
||||||
break
|
|
||||||
case "ssh":
|
|
||||||
protocolManager.SetProtocolStrategy(secureShellStrategy)
|
|
||||||
break
|
|
||||||
case "tcp":
|
|
||||||
protocolManager.SetProtocolStrategy(transmissionControlProtocolStrategy)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
log.Fatalf("Protocol %s not managed", beelzebubServiceConfiguration.Protocol)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err := protocolManager.InitService(beelzebubServiceConfiguration)
|
|
||||||
failOnError(err, fmt.Sprintf("Error during init protocol: %s, ", beelzebubServiceConfiguration.Protocol))
|
|
||||||
}
|
}
|
||||||
|
defer beelzebubBuilder.Close()
|
||||||
|
|
||||||
<-quit
|
<-quit
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,57 +41,3 @@ func failOnError(err error, msg string) {
|
|||||||
log.Fatalf("%s: %s", msg, err)
|
log.Fatalf("%s: %s", msg, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func traceStrategyStdoutAndRabbitMQ(event tracer.Event) {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"status": event.Status,
|
|
||||||
"event": event,
|
|
||||||
}).Info("New Event")
|
|
||||||
|
|
||||||
if channel != nil {
|
|
||||||
log.Debug("Push Event on queue")
|
|
||||||
eventJSON, err := json.Marshal(event)
|
|
||||||
failOnError(err, "Failed to Marshal Event")
|
|
||||||
|
|
||||||
queue, err := channel.QueueDeclare(
|
|
||||||
"event",
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
failOnError(err, "Failed to declare a queue")
|
|
||||||
|
|
||||||
err = channel.Publish(
|
|
||||||
"",
|
|
||||||
queue.Name,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
amqp.Publishing{
|
|
||||||
ContentType: "application/json",
|
|
||||||
Body: eventJSON,
|
|
||||||
})
|
|
||||||
failOnError(err, "Failed to publish a message")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureLoggingByConfigurations(configurations parser.Logging) *os.File {
|
|
||||||
file, err := os.OpenFile(configurations.LogsPath, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error opening file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetOutput(io.MultiWriter(os.Stdout, file))
|
|
||||||
|
|
||||||
log.SetFormatter(&log.JSONFormatter{
|
|
||||||
DisableTimestamp: configurations.LogDisableTimestamp,
|
|
||||||
})
|
|
||||||
log.SetReportCaller(configurations.DebugReportCaller)
|
|
||||||
if configurations.Debug {
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
} else {
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
}
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
@ -28,6 +28,10 @@ type Tracing struct {
|
|||||||
RabbitMQURI string `yaml:"rabbitMQURI"`
|
RabbitMQURI string `yaml:"rabbitMQURI"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Plugin struct {
|
||||||
|
OpenAPIChatGPTSecretKey string `yaml:"openAPIChatGPTSecretKey"`
|
||||||
|
}
|
||||||
|
|
||||||
type BeelzebubServiceConfiguration struct {
|
type BeelzebubServiceConfiguration struct {
|
||||||
ApiVersion string `yaml:"apiVersion"`
|
ApiVersion string `yaml:"apiVersion"`
|
||||||
Protocol string `yaml:"protocol"`
|
Protocol string `yaml:"protocol"`
|
||||||
@ -39,6 +43,7 @@ type BeelzebubServiceConfiguration struct {
|
|||||||
PasswordRegex string `yaml:"passwordRegex"`
|
PasswordRegex string `yaml:"passwordRegex"`
|
||||||
Description string `yaml:"description"`
|
Description string `yaml:"description"`
|
||||||
Banner string `yaml:"banner"`
|
Banner string `yaml:"banner"`
|
||||||
|
Plugin Plugin `yaml:"plugin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Command struct {
|
type Command struct {
|
||||||
@ -46,6 +51,7 @@ type Command struct {
|
|||||||
Handler string `yaml:"handler"`
|
Handler string `yaml:"handler"`
|
||||||
Headers []string `yaml:"headers"`
|
Headers []string `yaml:"headers"`
|
||||||
StatusCode int `yaml:"statusCode"`
|
StatusCode int `yaml:"statusCode"`
|
||||||
|
Plugin string `yaml:"plugin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type configurationsParser struct {
|
type configurationsParser struct {
|
||||||
|
113
plugin/OpenAiGPT.go
Normal file
113
plugin/OpenAiGPT.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ChatGPTPluginName = "OpenAIGPTLinuxTerminal"
|
||||||
|
const openAIGPTEndpoint = "https://api.openai.com/v1/completions"
|
||||||
|
|
||||||
|
type History struct {
|
||||||
|
Input, Output string
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenAIGPTVirtualTerminal struct {
|
||||||
|
Histories []History
|
||||||
|
OpenAPIChatGPTSecretKey string
|
||||||
|
client *resty.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (openAIGPTVirtualTerminal *OpenAIGPTVirtualTerminal) InjectDependency() {
|
||||||
|
if openAIGPTVirtualTerminal.client == nil {
|
||||||
|
openAIGPTVirtualTerminal.client = resty.New()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Choice struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Index int `json:"index"`
|
||||||
|
Logprobs interface{} `json:"logprobs"`
|
||||||
|
FinishReason string `json:"finish_reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type gptResponse struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Object string `json:"object"`
|
||||||
|
Created int `json:"created"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
Choices []Choice `json:"choices"`
|
||||||
|
Usage struct {
|
||||||
|
PromptTokens int `json:"prompt_tokens"`
|
||||||
|
CompletionTokens int `json:"completion_tokens"`
|
||||||
|
TotalTokens int `json:"total_tokens"`
|
||||||
|
} `json:"usage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type gptRequest struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
Temperature int `json:"temperature"`
|
||||||
|
MaxTokens int `json:"max_tokens"`
|
||||||
|
TopP int `json:"top_p"`
|
||||||
|
FrequencyPenalty int `json:"frequency_penalty"`
|
||||||
|
PresencePenalty int `json:"presence_penalty"`
|
||||||
|
Stop []string `json:"stop"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reference: https://www.engraved.blog/building-a-virtual-machine-inside/
|
||||||
|
const 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"
|
||||||
|
|
||||||
|
func buildPrompt(histories []History, command string) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
sb.WriteString(promptVirtualizeLinuxTerminal)
|
||||||
|
|
||||||
|
for _, history := range histories {
|
||||||
|
sb.WriteString(fmt.Sprintf("A:%s\n\nQ:%s\n\n", history.Input, history.Output))
|
||||||
|
}
|
||||||
|
// Append command to evaluate
|
||||||
|
sb.WriteString(fmt.Sprintf("A:%s\n\nQ:", command))
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (openAIGPTVirtualTerminal *OpenAIGPTVirtualTerminal) GetCompletions(command string) (string, error) {
|
||||||
|
requestJson, err := json.Marshal(gptRequest{
|
||||||
|
Model: "text-davinci-003",
|
||||||
|
Prompt: buildPrompt(openAIGPTVirtualTerminal.Histories, command),
|
||||||
|
Temperature: 0,
|
||||||
|
MaxTokens: 100,
|
||||||
|
TopP: 1,
|
||||||
|
FrequencyPenalty: 0,
|
||||||
|
PresencePenalty: 0,
|
||||||
|
Stop: []string{"\n"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if openAIGPTVirtualTerminal.OpenAPIChatGPTSecretKey == "" {
|
||||||
|
return "", errors.New("OpenAPIChatGPTSecretKey is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := openAIGPTVirtualTerminal.client.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(requestJson).
|
||||||
|
SetAuthToken(openAIGPTVirtualTerminal.OpenAPIChatGPTSecretKey).
|
||||||
|
SetResult(&gptResponse{}).
|
||||||
|
Post(openAIGPTEndpoint)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(response.Result().(*gptResponse).Choices) == 0 {
|
||||||
|
return "", errors.New("no choices")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result().(*gptResponse).Choices[0].Text, nil
|
||||||
|
}
|
82
plugin/OpenAiGPT_test.go
Normal file
82
plugin/OpenAiGPT_test.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/jarcoal/httpmock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildPromptEmptyHistory(t *testing.T) {
|
||||||
|
//Given
|
||||||
|
var histories []History
|
||||||
|
command := "pwd"
|
||||||
|
|
||||||
|
//When
|
||||||
|
prompt := buildPrompt(histories, command)
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Equal(t,
|
||||||
|
"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\nA:pwd\n\nQ:",
|
||||||
|
prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildPromptWithHistory(t *testing.T) {
|
||||||
|
//Given
|
||||||
|
var histories = []History{
|
||||||
|
{
|
||||||
|
Input: "cat hello.txt",
|
||||||
|
Output: "world",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "echo 1234",
|
||||||
|
Output: "1234",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
command := "pwd"
|
||||||
|
|
||||||
|
//When
|
||||||
|
prompt := buildPrompt(histories, command)
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Equal(t,
|
||||||
|
"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\nA:cat hello.txt\n\nQ:world\n\nA:echo 1234\n\nQ:1234\n\nA:pwd\n\nQ:",
|
||||||
|
prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildGetCompletions(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{
|
||||||
|
{
|
||||||
|
Text: "prova.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := OpenAIGPTVirtualTerminal{
|
||||||
|
OpenAPIChatGPTSecretKey: "sdjdnklfjndslkjanfk",
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
//When
|
||||||
|
str, err := openAIGPTVirtualTerminal.GetCompletions("ls")
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "prova.txt", str)
|
||||||
|
}
|
@ -47,7 +47,7 @@ func (httpStrategy HypertextTransferProtocolStrategy) Init(beelzebubServiceConfi
|
|||||||
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("Init service: %s", beelzebubServiceConfiguration.Description)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package protocols
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/parser"
|
"beelzebub/parser"
|
||||||
|
"beelzebub/plugin"
|
||||||
"beelzebub/tracer"
|
"beelzebub/tracer"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gliderlabs/ssh"
|
"github.com/gliderlabs/ssh"
|
||||||
@ -39,6 +40,7 @@ func (SSHStrategy *SecureShellStrategy) Init(beelzebubServiceConfiguration parse
|
|||||||
})
|
})
|
||||||
|
|
||||||
term := terminal.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
|
term := terminal.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
|
||||||
|
var histories []plugin.History
|
||||||
for {
|
for {
|
||||||
commandInput, err := term.ReadLine()
|
commandInput, err := term.ReadLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -64,7 +66,21 @@ func (SSHStrategy *SecureShellStrategy) Init(beelzebubServiceConfiguration parse
|
|||||||
}
|
}
|
||||||
|
|
||||||
if matched {
|
if matched {
|
||||||
term.Write(append([]byte(command.Handler), '\n'))
|
commandOutput := command.Handler
|
||||||
|
|
||||||
|
if command.Plugin == plugin.ChatGPTPluginName {
|
||||||
|
openAIGPTVirtualTerminal := plugin.OpenAIGPTVirtualTerminal{Histories: histories, OpenAPIChatGPTSecretKey: beelzebubServiceConfiguration.Plugin.OpenAPIChatGPTSecretKey}
|
||||||
|
openAIGPTVirtualTerminal.InjectDependency()
|
||||||
|
|
||||||
|
if commandOutput, err = openAIGPTVirtualTerminal.GetCompletions(commandInput); err != nil {
|
||||||
|
log.Errorf("Error GetCompletions: %s, %s", commandInput, err.Error())
|
||||||
|
commandOutput = "command not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
histories = append(histories, plugin.History{Input: commandInput, Output: commandOutput})
|
||||||
|
|
||||||
|
term.Write(append([]byte(commandOutput), '\n'))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user