mirror of
https://github.com/SecurityBrewery/catalyst.git
synced 2025-12-15 19:52:47 +01:00
Release catalyst
This commit is contained in:
93
test/data.go
Normal file
93
test/data.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/database"
|
||||
"github.com/SecurityBrewery/catalyst/database/migrations"
|
||||
"github.com/SecurityBrewery/catalyst/generated/models"
|
||||
"github.com/SecurityBrewery/catalyst/pointer"
|
||||
)
|
||||
|
||||
var bobSetting = &models.UserData{Email: pointer.String("bob@example.org"), Name: pointer.String("Bob Bad")}
|
||||
var bobForm = &models.UserForm{ID: "bob", Blocked: false, Roles: []string{"admin"}}
|
||||
var Bob = &models.UserResponse{ID: "bob", Blocked: false, Roles: []string{"admin"}}
|
||||
|
||||
func SetupTestData(ctx context.Context, db *database.Database) error {
|
||||
if err := db.UserDataCreate(ctx, "bob", bobSetting); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := db.UserCreate(ctx, bobForm); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.UserCreate(ctx, &models.UserForm{ID: "script", Roles: []string{"engineer"}, Apikey: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := db.TicketBatchCreate(ctx, []*models.TicketForm{
|
||||
{
|
||||
ID: pointer.Int64(8125),
|
||||
Created: parse("2021-10-02T18:04:59.078186+02:00"),
|
||||
Modified: parse("2021-10-02T18:04:59.078186+02:00"),
|
||||
Name: "phishing from selenafadel@von.com detected",
|
||||
Owner: pointer.String("demo"),
|
||||
References: []*models.Reference{{Href: "https://www.seniorleading-edge.name/users/efficient", Name: "recovery"}, {Href: "http://www.dynamicseamless.com/clicks-and-mortar", Name: "force"}, {Href: "http://www.leadscalable.biz/envisioneer", Name: "fund"}},
|
||||
Schema: pointer.String("{}"),
|
||||
Status: "closed",
|
||||
Type: "alert",
|
||||
}, {
|
||||
ID: pointer.Int64(8126),
|
||||
Created: parse("2021-10-02T18:04:59.078186+02:00"),
|
||||
Modified: parse("2021-10-02T18:04:59.078186+02:00"),
|
||||
Name: "Surfaceintroduce virus detected",
|
||||
Owner: pointer.String("demo"),
|
||||
References: []*models.Reference{{Href: "http://www.centralworld-class.io/synthesize", Name: "university"}, {Href: "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", Name: "goal"}, {Href: "http://www.chiefsyndicate.io/action-items", Name: "unemployment"}},
|
||||
Schema: pointer.String("{}"),
|
||||
Status: "closed",
|
||||
Type: "alert",
|
||||
}, {
|
||||
ID: pointer.Int64(8123),
|
||||
Created: parse("2021-10-02T18:04:59.078206+02:00"),
|
||||
Modified: parse("2021-10-02T18:04:59.078206+02:00"),
|
||||
Artifacts: []*models.Artifact{
|
||||
{Name: "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", Status: pointer.String("unknown")},
|
||||
{Name: "http://www.customerviral.io/scalable/vertical/killer", Status: pointer.String("clean")},
|
||||
{Name: "leadreintermediate.io", Status: pointer.String("malicious")},
|
||||
},
|
||||
Name: "live zebra",
|
||||
Owner: pointer.String("demo"),
|
||||
References: []*models.Reference{{Href: "https://www.leadmaximize.net/e-services/back-end", Name: "performance"}, {Href: "http://www.corporateinteractive.name/rich", Name: "autumn"}, {Href: "https://www.corporateintuitive.org/intuitive/platforms/integrate", Name: "suggest"}},
|
||||
Schema: pointer.String("{\n \"definitions\": {},\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://example.com/object1618746510.json\",\n \"title\": \"Event\",\n \"type\": \"object\",\n \"required\": [\n \"severity\",\n \"description\",\n \"tlp\"\n ],\n \"properties\": {\n \"severity\": {\n \"$id\": \"#root/severity\",\n \"title\": \"Severity\",\n \"type\": \"string\",\n \"default\": \"Medium\",\n \"nx-enum\": [\n \"Low\",\n \"Medium\",\n \"High\"\n ],\n \"x-cols\": 6,\n \"x-class\": \"pr-2\",\n \"x-display\": \"icon\",\n \"x-itemIcon\": \"icon\",\n \"oneOf\": [\n {\n \"const\": \"Low\",\n \"title\": \"Low\",\n \"icon\": \"mdi-chevron-up\"\n },\n {\n \"const\": \"Medium\",\n \"title\": \"Medium\",\n \"icon\": \"mdi-chevron-double-up\"\n },\n {\n \"const\": \"High\",\n \"title\": \"High\",\n \"icon\": \"mdi-chevron-triple-up\"\n }\n ]\n },\n \"tlp\": {\n \"$id\": \"#root/tlp\",\n \"title\": \"TLP\",\n \"type\": \"string\",\n \"nx-enum\": [\n \"White\",\n \"Green\",\n \"Amber\",\n \"Red\"\n ],\n \"x-cols\": 6,\n \"x-class\": \"pr-2\",\n \"x-display\": \"icon\",\n \"x-itemIcon\": \"icon\",\n \"oneOf\": [\n {\n \"const\": \"White\",\n \"title\": \"White\",\n \"icon\": \"mdi-alpha-w\"\n },\n {\n \"const\": \"Green\",\n \"title\": \"Green\",\n \"icon\": \"mdi-alpha-g\"\n },\n {\n \"const\": \"Amber\",\n \"title\": \"Amber\",\n \"icon\": \"mdi-alpha-a\"\n },\n {\n \"const\": \"Red\",\n \"title\": \"Red\",\n \"icon\": \"mdi-alpha-r\"\n }\n ]\n },\n \"description\": {\n \"$id\": \"#root/description\",\n \"title\": \"Description\",\n \"type\": \"string\",\n \"x-display\": \"textarea\",\n \"x-class\": \"pr-2\"\n }\n }\n}\n"),
|
||||
Status: "closed",
|
||||
Type: "incident",
|
||||
Playbooks: []*models.PlaybookTemplateForm{
|
||||
{Yaml: migrations.PhishingPlaybook},
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := db.RelatedCreate(ctx, 8125, 8126); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.PlaybookCreate(ctx, &models.PlaybookTemplateForm{Yaml: "name: Simple\ntasks:\n input:\n name: Enter something to hash\n type: input\n schema:\n title: Something\n type: object\n properties:\n something:\n type: string\n title: Something\n default: \"\"\n next:\n hash: \"something != ''\"\n\n hash:\n name: Hash the something\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['something']\"\n next:\n comment: \"hash != ''\"\n\n comment:\n name: Comment the hash\n type: automation\n automation: comment\n payload:\n default: \"playbook.tasks['hash'].data['hash']\"\n next:\n done: \"done\"\n\n done:\n name: You can close this case now\n type: task\n"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := db.LogCreate(ctx, "tickets/294511", "Fail run account resist lend solve incident centre priority temperature. Cause change distribution examine location technique shape partner milk customer. Rail tea plate soil report cook railway interpretation breath action. Exercise dream accept park conclusion addition shoot assistance may answer. Gold writer link stop combine hear power name commitment operation. Determine lifespan support grow degree henry exclude detail set religion. Direct library policy convention chain retain discover ride walk student. Gather proposal select march aspect play noise avoid encourage employ. Assessment preserve transport combine wish influence income guess run stand. Charge limit crime ignore statement foundation study issue stop claim."); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parse(s string) *time.Time {
|
||||
modified, err := time.Parse(time.RFC3339, s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &modified
|
||||
}
|
||||
104
test/server_test.go
Normal file
104
test/server_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
)
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
type args struct {
|
||||
method string
|
||||
url string
|
||||
data interface{}
|
||||
}
|
||||
type want struct {
|
||||
status int
|
||||
body interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{name: "GetUser not existing", args: args{method: http.MethodGet, url: "/api/users/123"}, want: want{status: http.StatusNotFound, body: gin.H{"error": "document not found"}}},
|
||||
{name: "ListUsers", args: args{method: http.MethodGet, url: "/api/users"}, want: want{status: http.StatusOK}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, _, _, _, _, _, _, server, cleanup, err := Server(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
setUser := func(context *gin.Context) {
|
||||
busdb.SetContext(context, Bob)
|
||||
}
|
||||
server.Use(setUser)
|
||||
|
||||
server.ConfigureRoutes()
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// setup request
|
||||
var req *http.Request
|
||||
if tt.args.data != nil {
|
||||
b, err := json.Marshal(tt.args.data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req = httptest.NewRequest(tt.args.method, tt.args.url, bytes.NewBuffer(b))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else {
|
||||
req = httptest.NewRequest(tt.args.method, tt.args.url, nil)
|
||||
}
|
||||
|
||||
// run request
|
||||
server.ServeHTTP(w, req)
|
||||
|
||||
result := w.Result()
|
||||
|
||||
// assert results
|
||||
if result.StatusCode != tt.want.status {
|
||||
t.Fatalf("Status got = %v, want %v", result.Status, tt.want.status)
|
||||
}
|
||||
if tt.want.status != http.StatusNoContent {
|
||||
jsonEqual(t, result.Body, tt.want.body)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func jsonEqual(t *testing.T, got io.Reader, want interface{}) {
|
||||
var j, j2 interface{}
|
||||
c, err := io.ReadAll(got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal(c, &j); err != nil {
|
||||
t.Fatal(string(c), err)
|
||||
}
|
||||
|
||||
b, err := json.Marshal(want)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = json.Unmarshal(b, &j2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(j2, j) {
|
||||
t.Errorf("Body got = %T:%v, want %T:%v", j, j, j2, j2)
|
||||
}
|
||||
}
|
||||
213
test/test.go
Normal file
213
test/test.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst"
|
||||
"github.com/SecurityBrewery/catalyst/bus"
|
||||
"github.com/SecurityBrewery/catalyst/database"
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
"github.com/SecurityBrewery/catalyst/generated/models"
|
||||
"github.com/SecurityBrewery/catalyst/generated/restapi"
|
||||
"github.com/SecurityBrewery/catalyst/hooks"
|
||||
"github.com/SecurityBrewery/catalyst/index"
|
||||
"github.com/SecurityBrewery/catalyst/pointer"
|
||||
"github.com/SecurityBrewery/catalyst/service"
|
||||
"github.com/SecurityBrewery/catalyst/storage"
|
||||
)
|
||||
|
||||
func Context() context.Context {
|
||||
w := httptest.NewRecorder()
|
||||
gctx, _ := gin.CreateTestContext(w)
|
||||
busdb.SetContext(gctx, Bob)
|
||||
return gctx
|
||||
}
|
||||
|
||||
func Config(ctx context.Context) (*catalyst.Config, error) {
|
||||
config := &catalyst.Config{
|
||||
IndexPath: "index.bleve",
|
||||
DB: &database.Config{
|
||||
Host: "http://localhost:8529",
|
||||
User: "root",
|
||||
Password: "foobar",
|
||||
},
|
||||
Storage: &storage.Config{
|
||||
Host: "http://localhost:9000",
|
||||
User: "minio",
|
||||
Password: "minio123",
|
||||
},
|
||||
Bus: &bus.Config{
|
||||
Host: "tcp://localhost:9001",
|
||||
Key: "A9RysEsPJni8RaHeg_K0FKXQNfBrUyw-",
|
||||
APIUrl: "http://localhost:8002/api",
|
||||
},
|
||||
UISettings: &models.Settings{
|
||||
ArtifactStates: []*models.Type{
|
||||
{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(models.TypeColorInfo)},
|
||||
{Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(models.TypeColorError)},
|
||||
{Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(models.TypeColorSuccess)},
|
||||
},
|
||||
TicketTypes: []*models.TicketTypeResponse{
|
||||
{ID: "alert", Icon: "mdi-alert", Name: "Alerts"},
|
||||
{ID: "incident", Icon: "mdi-radioactive", Name: "Incidents"},
|
||||
{ID: "investigation", Icon: "mdi-fingerprint", Name: "Forensic Investigations"},
|
||||
{ID: "hunt", Icon: "mdi-target", Name: "Threat Hunting"},
|
||||
},
|
||||
Version: "0.0.0-test",
|
||||
Tier: models.SettingsTierCommunity,
|
||||
Timeformat: "YYYY-MM-DDThh:mm:ss",
|
||||
},
|
||||
Secret: []byte("4ef5b29539b70233dd40c02a1799d25079595565e05a193b09da2c3e60ada1cd"),
|
||||
Auth: &catalyst.AuthConfig{
|
||||
OIDCIssuer: "http://localhost:9002/auth/realms/catalyst",
|
||||
OAuth2: &oauth2.Config{
|
||||
ClientID: "catalyst",
|
||||
ClientSecret: "13d4a081-7395-4f71-a911-bc098d8d3c45",
|
||||
RedirectURL: "http://localhost:8002/callback",
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
},
|
||||
// OIDCClaimUsername: "",
|
||||
// OIDCClaimEmail: "",
|
||||
// OIDCClaimName: "",
|
||||
// AuthBlockNew: false,
|
||||
// AuthDefaultRoles: nil,
|
||||
},
|
||||
}
|
||||
err := config.Auth.Load(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, err
|
||||
}
|
||||
|
||||
func Index(t *testing.T) (*index.Index, func(), error) {
|
||||
dir, err := os.MkdirTemp("", "catalyst-test-"+cleanName(t))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
catalystIndex, err := index.New(path.Join(dir, "index.bleve"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return catalystIndex, func() { os.RemoveAll(dir) }, nil
|
||||
}
|
||||
|
||||
func Bus(t *testing.T) (context.Context, *catalyst.Config, *bus.Bus, error) {
|
||||
ctx := Context()
|
||||
|
||||
config, err := Config(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
catalystBus, err := bus.New(config.Bus)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return ctx, config, catalystBus, err
|
||||
}
|
||||
|
||||
func DB(t *testing.T) (context.Context, *catalyst.Config, *bus.Bus, *index.Index, *storage.Storage, *database.Database, func(), error) {
|
||||
ctx, config, rbus, err := Bus(t)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
catalystStorage, err := storage.New(config.Storage)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
catalystIndex, cleanup, err := Index(t)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
c := config.DB
|
||||
c.Name = cleanName(t)
|
||||
db, err := database.New(ctx, catalystIndex, rbus, &hooks.Hooks{
|
||||
DatabaseAfterConnectFuncs: []func(ctx context.Context, client driver.Client, name string){Clear},
|
||||
}, c)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
_, err = db.JobCreate(ctx, "99cd67131b48", &models.JobForm{
|
||||
Automation: "hash.sha1",
|
||||
Payload: "test",
|
||||
Origin: nil,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
return ctx, config, rbus, catalystIndex, catalystStorage, db, func() {
|
||||
err := db.Remove(context.Background())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
cleanup()
|
||||
}, err
|
||||
}
|
||||
|
||||
func Service(t *testing.T) (context.Context, *catalyst.Config, *bus.Bus, *index.Index, *storage.Storage, *database.Database, *service.Service, func(), error) {
|
||||
ctx, config, rbus, catalystIndex, catalystStorage, db, cleanup, err := DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
catalystService, err := service.New(rbus, db, catalystStorage, config.UISettings)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return ctx, config, rbus, catalystIndex, catalystStorage, db, catalystService, cleanup, err
|
||||
}
|
||||
|
||||
func Server(t *testing.T) (context.Context, *catalyst.Config, *bus.Bus, *index.Index, *storage.Storage, *database.Database, *service.Service, *restapi.Server, func(), error) {
|
||||
ctx, config, rbus, catalystIndex, catalystStorage, db, catalystService, cleanup, err := Service(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
catalystServer := restapi.New(catalystService, &restapi.Config{Address: "0.0.0.0:8000", InsecureHTTP: true})
|
||||
|
||||
return ctx, config, rbus, catalystIndex, catalystStorage, db, catalystService, catalystServer, cleanup, err
|
||||
}
|
||||
|
||||
func cleanName(t *testing.T) string {
|
||||
name := t.Name()
|
||||
name = strings.ReplaceAll(name, " ", "")
|
||||
name = strings.ReplaceAll(name, "/", "_")
|
||||
return strings.ReplaceAll(name, "#", "_")
|
||||
}
|
||||
|
||||
func Clear(ctx context.Context, client driver.Client, name string) {
|
||||
if exists, _ := client.DatabaseExists(ctx, name); exists {
|
||||
if db, err := client.Database(ctx, name); err == nil {
|
||||
if exists, _ = db.GraphExists(ctx, database.TicketArtifactsGraphName); exists {
|
||||
if g, err := db.Graph(ctx, database.TicketArtifactsGraphName); err == nil {
|
||||
if err := g.Remove(ctx); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := db.Remove(ctx); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user