Add simple auth (#186)

This commit is contained in:
Jonas Plum authored and GitHub committed 2022-06-13 18:13:31 +02:00
1 parent 4883646f39
commit 9f1041d7ef
43 files changed
+1304 -622

No files matched your search

+33 -8
View File
@@ -66,7 +66,7 @@ jobs:
- run: |
mkdir -p ui/dist/img
touch ui/dist/index.html ui/dist/favicon.ico ui/dist/manifest.json ui/dist/img/fake.png
- run: docker-compose up -d
- run: docker-compose -f docker-compose-with-keycloak.yml up --quiet-pull --detach
working-directory: dev
- name: Install ArangoDB
run: |
@@ -95,6 +95,9 @@ jobs:
cypress:
runs-on: ubuntu-latest
strategy:
matrix:
auth: [ simple, keycloak ]
steps:
- name: Checkout
uses: actions/checkout@v3
@@ -108,21 +111,34 @@ jobs:
yarn install
yarn serve &
working-directory: ui
- uses: cugu/wait_for_response@v1.8.0
- name: Wait for frontend
uses: cugu/wait_for_response@v1.12.0
with:
url: 'http://localhost:8080'
responseCode: 200
# run containers
# run containers
- run: sed -i 's/host.docker.internal/172.17.0.1/g' dev/nginx.conf
shell: bash
- run: sed -i 's/host.docker.internal/172.17.0.1/g' dev/nginx-with-keycloak.conf
shell: bash
- run: docker-compose up --quiet-pull --detach
working-directory: dev
shell: bash
- uses: cugu/wait_for_response@v1.8.0
if: ${{ matrix.auth == 'simple' }}
- run: docker-compose -f docker-compose-with-keycloak.yml up --quiet-pull --detach
working-directory: dev
shell: bash
if: ${{ matrix.auth == 'keycloak' }}
- name: Wait for keycloak
uses: cugu/wait_for_response@v1.12.0
with:
url: 'http://localhost:9002/auth/realms/catalyst'
responseCode: 200
verbose: true
timeout: 3m
interval: 10s
if: ${{ matrix.auth == 'keycloak' }}
# run catalyst
- run: |
@@ -130,16 +146,25 @@ jobs:
touch ui/dist/index.html ui/dist/favicon.ico ui/dist/manifest.json ui/dist/img/fake.png
- run: go mod download
- run: bash start_dev.sh &
- uses: cugu/wait_for_response@v1.8.0
working-directory: dev
if: ${{ matrix.auth == 'simple' }}
- run: bash start_dev_with_keycloak.sh &
working-directory: dev
if: ${{ matrix.auth == 'keycloak' }}
- name: Wait for catalyst
uses: cugu/wait_for_response@v1.12.0
with:
url: 'http://localhost:8000'
method: GET
responseCode: 302
verbose: true
timeout: 3m # 3 minutes
interval: 10s # every 10 seconds
timeout: 3m
interval: 10s
# run cypress
- run: ./node_modules/.bin/cypress run
- run: ./node_modules/.bin/cypress run --spec "cypress/integration/catalyst.js"
env:
CYPRESS_AUTH: ${{ matrix.auth }}
working-directory: ui
- uses: actions/upload-artifact@v3
+1
View File
@@ -101,6 +101,7 @@ linters-settings:
- go-driver.Cursor
- go-driver.Collection
- go-driver.Database
- go-driver.Client
- chi.Router
issues:
exclude-rules:
+145 -51
View File
@@ -1,14 +1,17 @@
package catalyst
package auth
import (
"context"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"strings"
"time"
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/exp/slices"
@@ -22,25 +25,32 @@ import (
"github.com/SecurityBrewery/catalyst/role"
)
type AuthConfig struct {
OIDCIssuer string
OAuth2 *oauth2.Config
type Config struct {
SimpleAuthEnable bool
APIKeyAuthEnable bool
OIDCAuthEnable bool
OIDCClaimUsername string
OIDCClaimEmail string
// OIDCClaimGroups string
OIDCClaimName string
AuthBlockNew bool
AuthDefaultRoles []role.Role
AuthAdminUsers []string
OIDCIssuer string
OAuth2 *oauth2.Config
UserCreateConfig *UserCreateConfig
provider *oidc.Provider
}
func (c *AuthConfig) Verifier(ctx context.Context) (*oidc.IDTokenVerifier, error) {
type UserCreateConfig struct {
AuthBlockNew bool
AuthDefaultRoles []role.Role
AuthAdminUsers []string
OIDCClaimUsername string
OIDCClaimEmail string
OIDCClaimName string
// OIDCClaimGroups string
}
func (c *Config) Verifier(ctx context.Context) (*oidc.IDTokenVerifier, error) {
if c.provider == nil {
err := c.Load(ctx)
if err != nil {
if err := c.Load(ctx); err != nil {
return nil, err
}
}
@@ -48,37 +58,55 @@ func (c *AuthConfig) Verifier(ctx context.Context) (*oidc.IDTokenVerifier, error
return c.provider.Verifier(&oidc.Config{SkipClientIDCheck: true}), nil
}
func (c *AuthConfig) Load(ctx context.Context) error {
provider, err := oidc.NewProvider(ctx, c.OIDCIssuer)
if err != nil {
return err
func (c *Config) Load(ctx context.Context) error {
for {
provider, err := oidc.NewProvider(ctx, c.OIDCIssuer)
if err == nil {
c.provider = provider
c.OAuth2.Endpoint = provider.Endpoint()
break
}
if errors.Is(err, context.DeadlineExceeded) {
return errors.New("could not load provider")
}
log.Printf("could not load oidc provider: %s, retrying in 10 seconds\n", err)
time.Sleep(time.Second * 10)
}
c.provider = provider
c.OAuth2.Endpoint = provider.Endpoint()
return nil
}
func Authenticate(db *database.Database, config *AuthConfig) func(next http.Handler) http.Handler {
func Authenticate(db *database.Database, config *Config, jar *Jar) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
keyHeader := r.Header.Get("PRIVATE-TOKEN")
authHeader := r.Header.Get("User")
authHeader := r.Header.Get("Authorization")
switch {
case keyHeader != "":
keyAuth(db, keyHeader)(next).ServeHTTP(w, r)
if config.APIKeyAuthEnable {
keyAuth(db, keyHeader)(next).ServeHTTP(w, r)
} else {
api.JSONErrorStatus(w, http.StatusUnauthorized, errors.New("API Key authentication not enabled"))
}
case authHeader != "":
iss := config.OIDCIssuer
bearerAuth(db, authHeader, iss, config)(next).ServeHTTP(w, r)
if config.OIDCAuthEnable {
iss := config.OIDCIssuer
bearerAuth(db, authHeader, iss, config, jar)(next).ServeHTTP(w, r)
} else {
api.JSONErrorStatus(w, http.StatusUnauthorized, errors.New("OIDC authentication not enabled"))
}
default:
sessionAuth(db, config)(next).ServeHTTP(w, r)
sessionAuth(db, config, jar)(next).ServeHTTP(w, r)
}
})
}
}
func bearerAuth(db *database.Database, authHeader string, iss string, config *AuthConfig) func(next http.Handler) http.Handler {
func bearerAuth(db *database.Database, authHeader string, iss string, config *Config, jar *Jar) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.HasPrefix(authHeader, "Bearer ") {
@@ -99,7 +127,7 @@ func bearerAuth(db *database.Database, authHeader string, iss string, config *Au
// return
// }
setClaimsCookie(w, claims)
jar.setClaimsCookie(w, claims)
r, err := setContextClaims(r, db, claims, config)
if err != nil {
@@ -118,7 +146,7 @@ func keyAuth(db *database.Database, keyHeader string) func(next http.Handler) ht
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h := fmt.Sprintf("%x", sha256.Sum256([]byte(keyHeader)))
key, err := db.UserByHash(r.Context(), h)
key, err := db.UserAPIKeyByHash(r.Context(), h)
if err != nil {
api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("could not verify private token: %w", err))
@@ -132,17 +160,19 @@ func keyAuth(db *database.Database, keyHeader string) func(next http.Handler) ht
}
}
func sessionAuth(db *database.Database, config *AuthConfig) func(next http.Handler) http.Handler {
func sessionAuth(db *database.Database, config *Config, jar *Jar) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, noCookie, err := claimsCookie(r)
claims, noCookie, err := jar.claimsCookie(r)
if err != nil {
deleteClaimsCookie(w)
api.JSONError(w, err)
return
}
if noCookie {
redirectToLogin(w, r, config.OAuth2)
redirectToLogin(config, jar)(w, r)
return
}
@@ -159,8 +189,8 @@ func sessionAuth(db *database.Database, config *AuthConfig) func(next http.Handl
}
}
func setContextClaims(r *http.Request, db *database.Database, claims map[string]any, config *AuthConfig) (*http.Request, error) {
newUser, newSetting, err := mapUserAndSettings(claims, config)
func setContextClaims(r *http.Request, db *database.Database, claims map[string]any, config *Config) (*http.Request, error) {
newUser, newSetting, err := mapUserAndSettings(claims, config.UserCreateConfig)
if err != nil {
return nil, err
}
@@ -174,8 +204,7 @@ func setContextClaims(r *http.Request, db *database.Database, claims map[string]
return nil, err
}
_, err = db.UserDataGetOrCreate(r.Context(), newUser.ID, newSetting)
if err != nil {
if _, err = db.UserDataGetOrCreate(r.Context(), newUser.ID, newSetting); err != nil {
return nil, err
}
@@ -191,7 +220,7 @@ func setContextUser(r *http.Request, user *model.UserResponse, hooks *hooks.Hook
return busdb.SetContext(r, user)
}
func mapUserAndSettings(claims map[string]any, config *AuthConfig) (*model.UserForm, *model.UserData, error) {
func mapUserAndSettings(claims map[string]any, config *UserCreateConfig) (*model.UserForm, *model.UserData, error) {
// handle Bearer tokens
// if typ, ok := claims["typ"]; ok && typ == "Bearer" {
// return &model.User{
@@ -207,6 +236,7 @@ func mapUserAndSettings(claims map[string]any, config *AuthConfig) (*model.UserF
if err != nil {
return nil, nil, err
}
email, err := getString(claims, config.OIDCClaimEmail)
if err != nil {
email = ""
@@ -244,19 +274,35 @@ func getString(m map[string]any, key string) (string, error) {
return "", fmt.Errorf("mapping of %s failed, missing value", key)
}
func redirectToLogin(w http.ResponseWriter, r *http.Request, oauth2Config *oauth2.Config) {
state, err := state()
if err != nil {
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("generating state failed"))
return
func redirectToLogin(config *Config, jar *Jar) func(http.ResponseWriter, *http.Request) {
if config.SimpleAuthEnable {
return func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusFound)
}
}
setStateCookie(w, state)
if config.OIDCAuthEnable {
return redirectToOIDCLogin(config, jar)
}
http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
return func(writer http.ResponseWriter, request *http.Request) {
api.JSONErrorStatus(writer, http.StatusForbidden, errors.New("unauthenticated"))
}
}
return
func redirectToOIDCLogin(config *Config, jar *Jar) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
state, err := state()
if err != nil {
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("generating state failed"))
return
}
jar.setStateCookie(w, state)
http.Redirect(w, r, config.OAuth2.AuthCodeURL(state), http.StatusFound)
}
}
func AuthorizeBlockedUser() func(http.Handler) http.Handler {
@@ -301,9 +347,56 @@ func AuthorizeRole(roles []string) func(http.Handler) http.Handler {
}
}
func callback(config *AuthConfig) http.HandlerFunc {
func login(db *database.Database, jar *Jar) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
state, err := stateCookie(r)
type credentials struct {
Username string
Password string
}
cr := credentials{}
if err := json.NewDecoder(r.Body).Decode(&cr); err != nil {
api.JSONErrorStatus(w, http.StatusUnauthorized, errors.New("wrong username or password"))
return
}
user, err := db.UserByIDAndPassword(r.Context(), cr.Username, cr.Password)
if err != nil {
api.JSONErrorStatus(w, http.StatusUnauthorized, errors.New("wrong username or password"))
return
}
userdata, err := db.UserDataGet(r.Context(), user.ID)
if err != nil {
api.JSONErrorStatus(w, http.StatusUnauthorized, errors.New("no userdata"))
return
}
jar.setClaimsCookie(w, map[string]any{
"preferred_username": user.ID,
"name": userdata.Name,
"email": userdata.Email,
})
b, _ := json.Marshal(map[string]string{"login": "successful"})
_, _ = w.Write(b)
}
}
func logout() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
deleteClaimsCookie(w)
http.Redirect(w, r, "/", http.StatusFound)
}
}
func callback(config *Config, jar *Jar) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
state, err := jar.stateCookie(r)
if err != nil || state == "" {
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("state missing"))
@@ -338,9 +431,9 @@ func callback(config *AuthConfig) http.HandlerFunc {
return
}
setClaimsCookie(w, claims)
jar.setClaimsCookie(w, claims)
http.Redirect(w, r, "/", http.StatusFound)
http.Redirect(w, r, "/ui/", http.StatusFound)
}
}
@@ -353,11 +446,12 @@ func state() (string, error) {
return base64.URLEncoding.EncodeToString(rnd), nil
}
func verifyClaims(r *http.Request, config *AuthConfig, rawIDToken string) (map[string]any, *api.HTTPError) {
func verifyClaims(r *http.Request, config *Config, rawIDToken string) (map[string]any, *api.HTTPError) {
verifier, err := config.Verifier(r.Context())
if err != nil {
return nil, &api.HTTPError{Status: http.StatusUnauthorized, Internal: fmt.Errorf("could not verify: %w", err)}
}
authToken, err := verifier.Verify(r.Context(), rawIDToken)
if err != nil {
return nil, &api.HTTPError{Status: http.StatusInternalServerError, Internal: fmt.Errorf("could not verify bearer token: %w", err)}
+84
View File
@@ -0,0 +1,84 @@
package auth
import (
"log"
"net/http"
"github.com/gorilla/securecookie"
"golang.org/x/crypto/argon2"
"github.com/SecurityBrewery/catalyst/generated/time"
)
const (
stateSessionCookie = "state"
userSessionCookie = "user"
)
type Jar struct {
store *securecookie.SecureCookie
}
func NewJar(secret []byte) *Jar {
hashSalt := securecookie.GenerateRandomKey(64)
blockSalt := securecookie.GenerateRandomKey(64)
return &Jar{
store: securecookie.New(
argon2.IDKey(secret, hashSalt, 1, 64*1024, 4, 64),
argon2.IDKey(secret, blockSalt, 1, 64*1024, 4, 32),
),
}
}
func (j *Jar) setStateCookie(w http.ResponseWriter, state string) {
encoded, err := j.store.Encode(userSessionCookie, state)
if err != nil {
log.Println(err)
return
}
tomorrow := time.Now().AddDate(0, 0, 1)
http.SetCookie(w, &http.Cookie{Name: stateSessionCookie, Value: encoded, Path: "/", Expires: tomorrow})
}
func (j *Jar) stateCookie(r *http.Request) (string, error) {
stateCookie, err := r.Cookie(stateSessionCookie)
if err != nil {
return "", err
}
var state string
err = j.store.Decode(userSessionCookie, stateCookie.Value, &state)
return state, err
}
func (j *Jar) setClaimsCookie(w http.ResponseWriter, claims map[string]any) {
encoded, err := j.store.Encode(userSessionCookie, claims)
if err != nil {
log.Println(err)
return
}
tomorrow := time.Now().AddDate(0, 0, 1)
http.SetCookie(w, &http.Cookie{Name: userSessionCookie, Value: encoded, Path: "/", Expires: tomorrow})
}
func deleteClaimsCookie(w http.ResponseWriter) {
http.SetCookie(w, &http.Cookie{Name: userSessionCookie, Value: "", MaxAge: -1})
}
func (j *Jar) claimsCookie(r *http.Request) (map[string]any, bool, error) {
userCookie, err := r.Cookie(userSessionCookie)
if err != nil {
return nil, true, nil
}
var claims map[string]any
err = j.store.Decode(userSessionCookie, userCookie.Value, &claims)
return claims, false, err
}
+43
View File
@@ -0,0 +1,43 @@
package auth
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi"
"github.com/SecurityBrewery/catalyst/database"
)
func Server(config *Config, catalystDatabase *database.Database, jar *Jar) *chi.Mux {
server := chi.NewRouter()
server.Get("/config", hasOIDC(config))
if config.OIDCAuthEnable {
server.Get("/callback", callback(config, jar))
server.Get("/oidclogin", redirectToOIDCLogin(config, jar))
}
if config.SimpleAuthEnable {
server.Post("/login", login(catalystDatabase, jar))
}
server.Post("/logout", logout())
return server
}
func hasOIDC(config *Config) func(writer http.ResponseWriter, request *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
b, err := json.Marshal(map[string]any{
"simple": config.SimpleAuthEnable,
"oidc": config.OIDCAuthEnable,
})
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
return
}
_, _ = writer.Write(b)
}
}
+16 -19
View File
@@ -2,9 +2,9 @@ package main
import (
"context"
"fmt"
"log"
"net/http"
"strings"
"github.com/arangodb/go-driver"
@@ -41,37 +41,34 @@ func main() {
log.Fatal(err)
}
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "eve", Roles: []string{"admin"}})
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "eve", Roles: []string{"admin"}, Password: pointer.String("eve")})
_ = theCatalyst.DB.UserDataCreate(context.Background(), "eve", &model.UserData{
Name: pointer.String("Eve"),
Email: pointer.String("eve@example.com"),
Image: &avatarEve,
})
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "kevin", Roles: []string{"admin"}})
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "kevin", Roles: []string{"admin"}, Password: pointer.String("kevin")})
_ = theCatalyst.DB.UserDataCreate(context.Background(), "kevin", &model.UserData{
Name: pointer.String("Kevin"),
Email: pointer.String("kevin@example.com"),
Image: &avatarKevin,
})
// proxy static requests
middlewares := []func(next http.Handler) http.Handler{
catalyst.Authenticate(theCatalyst.DB, config.Auth),
catalyst.AuthorizeBlockedUser(),
}
theCatalyst.Server.With(middlewares...).NotFound(func(writer http.ResponseWriter, request *http.Request) {
var handler http.Handler = http.HandlerFunc(api.Proxy("http://localhost:8080/static/"))
if strings.HasPrefix(request.URL.Path, "/static/") {
handler = http.StripPrefix("/static/", handler)
} else {
request.URL.Path = "/"
}
handler.ServeHTTP(writer, request)
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "tom", Roles: []string{"admin"}, Password: pointer.String("tom")})
_ = theCatalyst.DB.UserDataCreate(context.Background(), "tom", &model.UserData{
Name: pointer.String("tom"),
Email: pointer.String("tom@example.com"),
Image: &avatarKevin,
})
if err := http.ListenAndServe(":8000", theCatalyst.Server); err != nil {
// proxy static requests
theCatalyst.Server.Get("/ui/*", func(writer http.ResponseWriter, request *http.Request) {
log.Println("proxy request", request.URL.Path)
api.Proxy("http://localhost:8080/")(writer, request)
})
if err := http.ListenAndServe(fmt.Sprintf(":%d", config.Port), theCatalyst.Server); err != nil {
log.Fatal(err)
}
}
+8 -1
View File
@@ -1,12 +1,16 @@
package main
import (
"fmt"
"io/fs"
"log"
"net/http"
"github.com/SecurityBrewery/catalyst"
"github.com/SecurityBrewery/catalyst/cmd"
"github.com/SecurityBrewery/catalyst/generated/api"
"github.com/SecurityBrewery/catalyst/hooks"
"github.com/SecurityBrewery/catalyst/ui"
)
func main() {
@@ -22,7 +26,10 @@ func main() {
log.Fatal(err)
}
if err := http.ListenAndServe(":8000", theCatalyst.Server); err != nil {
fsys, _ := fs.Sub(ui.UI, "dist")
theCatalyst.Server.Get("/ui/*", api.Static(fsys))
if err := http.ListenAndServe(fmt.Sprintf(":%d", config.Port), theCatalyst.Server); err != nil {
log.Fatal(err)
}
}
+38 -17
View File
@@ -8,6 +8,7 @@ import (
"golang.org/x/oauth2"
"github.com/SecurityBrewery/catalyst"
"github.com/SecurityBrewery/catalyst/auth"
"github.com/SecurityBrewery/catalyst/database"
"github.com/SecurityBrewery/catalyst/role"
"github.com/SecurityBrewery/catalyst/storage"
@@ -18,7 +19,17 @@ type CLI struct {
ExternalAddress string `env:"EXTERNAL_ADDRESS" required:""`
CatalystAddress string `env:"CATALYST_ADDRESS" default:"http://catalyst:8000"`
Network string `env:"CATALYST_NETWORK" default:"catalyst"`
Port int `env:"PORT" default:"8000"`
AuthBlockNew bool `env:"AUTH_BLOCK_NEW" default:"true" help:"Block newly created users"`
AuthDefaultRoles []string `env:"AUTH_DEFAULT_ROLES" help:"Default roles for new users"`
AuthAdminUsers []string `env:"AUTH_ADMIN_USERS" help:"Username of admins"`
InitialAPIKey string `env:"INITIAL_API_KEY"`
SimpleAuthEnable bool `env:"SIMPLE_AUTH_ENABLE" default:"true"`
APIKeyAuthEnable bool `env:"API_KEY_AUTH_ENABLE" default:"true"`
OIDCEnable bool `env:"OIDC_ENABLE" default:"false"`
OIDCIssuer string `env:"OIDC_ISSUER" required:""`
OIDCClientID string `env:"OIDC_CLIENT_ID" default:"catalyst"`
OIDCClientSecret string `env:"OIDC_CLIENT_SECRET" required:""`
@@ -26,9 +37,6 @@ type CLI struct {
OIDCClaimUsername string `env:"OIDC_CLAIM_USERNAME" default:"preferred_username" help:"username field in the OIDC claim"`
OIDCClaimEmail string `env:"OIDC_CLAIM_EMAIL" default:"email" help:"email field in the OIDC claim"`
OIDCClaimName string `env:"OIDC_CLAIM_NAME" default:"name" help:"name field in the OIDC claim"`
AuthBlockNew bool `env:"AUTH_BLOCK_NEW" default:"true" help:"Block newly created users"`
AuthDefaultRoles []string `env:"AUTH_DEFAULT_ROLES" help:"Default roles for new users"`
AuthAdminUsers []string `env:"AUTH_ADMIN_USERS" help:"Username of admins"`
IndexPath string `env:"INDEX_PATH" default:"index.bleve" help:"Path for the bleve index"`
@@ -39,8 +47,6 @@ type CLI struct {
S3Host string `env:"S3_HOST" default:"http://minio:9000" name:"s3-host"`
S3User string `env:"S3_USER" default:"minio" name:"s3-user"`
S3Password string `env:"S3_PASSWORD" required:"" name:"s3-password"`
InitialAPIKey string `env:"INITIAL_API_KEY"`
}
func ParseCatalystConfig() (*catalyst.Config, error) {
@@ -61,22 +67,37 @@ func MapConfig(cli CLI) (*catalyst.Config, error) {
scopes := slices.Compact(append([]string{oidc.ScopeOpenID, "profile", "email"}, cli.OIDCScopes...))
config := &catalyst.Config{
IndexPath: cli.IndexPath,
Network: cli.Network,
DB: &database.Config{Host: cli.ArangoDBHost, User: cli.ArangoDBUser, Password: cli.ArangoDBPassword},
IndexPath: cli.IndexPath,
Network: cli.Network,
DB: &database.Config{
Host: cli.ArangoDBHost,
User: cli.ArangoDBUser,
Password: cli.ArangoDBPassword,
},
Storage: &storage.Config{Host: cli.S3Host, User: cli.S3User, Password: cli.S3Password},
Secret: []byte(cli.Secret),
ExternalAddress: cli.ExternalAddress,
InternalAddress: cli.CatalystAddress,
Auth: &catalyst.AuthConfig{
OIDCIssuer: cli.OIDCIssuer,
OAuth2: &oauth2.Config{ClientID: cli.OIDCClientID, ClientSecret: cli.OIDCClientSecret, RedirectURL: cli.ExternalAddress + "/callback", Scopes: scopes},
OIDCClaimUsername: cli.OIDCClaimUsername,
OIDCClaimEmail: cli.OIDCClaimEmail,
OIDCClaimName: cli.OIDCClaimName,
AuthBlockNew: cli.AuthBlockNew,
AuthDefaultRoles: roles,
AuthAdminUsers: cli.AuthAdminUsers,
Port: cli.Port,
Auth: &auth.Config{
SimpleAuthEnable: cli.SimpleAuthEnable,
APIKeyAuthEnable: cli.APIKeyAuthEnable,
OIDCAuthEnable: cli.OIDCEnable,
OIDCIssuer: cli.OIDCIssuer,
OAuth2: &oauth2.Config{
ClientID: cli.OIDCClientID,
ClientSecret: cli.OIDCClientSecret,
RedirectURL: cli.ExternalAddress + "/auth/callback",
Scopes: scopes,
},
UserCreateConfig: &auth.UserCreateConfig{
AuthBlockNew: cli.AuthBlockNew,
AuthDefaultRoles: roles,
AuthAdminUsers: cli.AuthAdminUsers,
OIDCClaimUsername: cli.OIDCClaimUsername,
OIDCClaimEmail: cli.OIDCClaimEmail,
OIDCClaimName: cli.OIDCClaimName,
},
},
InitialAPIKey: cli.InitialAPIKey,
}
-57
View File
@@ -1,57 +0,0 @@
package catalyst
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
)
const (
stateSessionCookie = "state"
userSessionCookie = "user"
)
func setStateCookie(w http.ResponseWriter, state string) {
http.SetCookie(w, &http.Cookie{Name: stateSessionCookie, Value: state})
}
func stateCookie(r *http.Request) (string, error) {
stateCookie, err := r.Cookie(stateSessionCookie)
if err != nil {
return "", err
}
return stateCookie.Value, nil
}
func setClaimsCookie(w http.ResponseWriter, claims map[string]any) {
b, err := json.Marshal(claims)
if err != nil {
log.Println(err)
return
}
http.SetCookie(w, &http.Cookie{Name: userSessionCookie, Value: base64.StdEncoding.EncodeToString(b)})
}
func claimsCookie(r *http.Request) (map[string]any, bool, error) {
userCookie, err := r.Cookie(userSessionCookie)
if err != nil {
return nil, true, nil
}
b, err := base64.StdEncoding.DecodeString(userCookie.Value)
if err != nil {
return nil, false, fmt.Errorf("could not decode cookie: %w", err)
}
var claims map[string]any
if err := json.Unmarshal(b, &claims); err != nil {
return nil, false, errors.New("claims not in session")
}
return claims, false, err
}
+44 -13
View File
@@ -2,8 +2,10 @@ package database
import (
"context"
"errors"
"fmt"
"log"
"time"
"github.com/arangodb/go-driver"
"github.com/arangodb/go-driver/http"
@@ -67,17 +69,25 @@ func New(ctx context.Context, index *index.Index, bus *bus.Bus, hooks *hooks.Hoo
name = Name
}
conn, err := http.NewConnection(http.ConnectionConfig{Endpoints: []string{config.Host}})
if err != nil {
return nil, err
}
var err error
var client driver.Client
for {
deadline, ok := ctx.Deadline()
if ok && time.Until(deadline) < 0 {
return nil, context.DeadlineExceeded
}
client, err := driver.NewClient(driver.ClientConfig{
Connection: conn,
Authentication: driver.BasicAuthentication(config.User, config.Password),
})
if err != nil {
return nil, err
client, err = getClient(ctx, config)
if err == nil {
break
}
if errors.Is(err, context.DeadlineExceeded) {
return nil, errors.New("could not load database, connection timed out")
}
log.Printf("could not connect to database: %s, retrying in 10 seconds\n", err)
time.Sleep(time.Second * 10)
}
hooks.DatabaseAfterConnect(ctx, client, name)
@@ -162,10 +172,31 @@ func New(ctx context.Context, index *index.Index, bus *bus.Bus, hooks *hooks.Hoo
return db, nil
}
func getClient(ctx context.Context, config *Config) (driver.Client, error) {
conn, err := http.NewConnection(http.ConnectionConfig{Endpoints: []string{config.Host}})
if err != nil {
return nil, err
}
client, err := driver.NewClient(driver.ClientConfig{
Connection: conn,
Authentication: driver.BasicAuthentication(config.User, config.Password),
})
if err != nil {
return nil, err
}
if _, err := client.Version(ctx); err != nil {
return nil, err
}
return client, nil
}
func SetupDB(ctx context.Context, client driver.Client, dbName string) (driver.Database, error) {
databaseExists, err := client.DatabaseExists(ctx, dbName)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not check if database exists: %w", err)
}
var db driver.Database
@@ -175,12 +206,12 @@ func SetupDB(ctx context.Context, client driver.Client, dbName string) (driver.D
db, err = client.Database(ctx, dbName)
}
if err != nil {
return nil, err
return nil, fmt.Errorf("could not create database: %w", err)
}
collectionExists, err := db.CollectionExists(ctx, migrations.MigrationCollection)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not check if collection exists: %w", err)
}
if !collectionExists {
+2
View File
@@ -60,6 +60,8 @@ func generateMigrations() ([]Migration, error) {
&createCollection{ID: "create-dashboard-collection", Name: "dashboards", DataType: "dashboards", Schema: `{"type":"object","properties":{"name":{"type":"string"},"widgets":{"items":{"type":"object","properties":{"aggregation":{"type":"string"},"filter":{"type":"string"},"name":{"type":"string"},"type":{"enum":[ "bar", "line", "pie" ]},"width": { "type": "integer", "minimum": 1, "maximum": 12 }},"required":["name","aggregation", "type", "width"]},"type":"array"}},"required":["name","widgets"]}`},
&updateDocument[model.Settings]{ID: "update-settings-global-1", Collection: "settings", Key: "global", Document: &model.Settings{ArtifactStates: []*model.Type{{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)}, {Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)}, {Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)}}, ArtifactKinds: []*model.Type{{Icon: "mdi-server", ID: "asset", Name: "Asset"}, {Icon: "mdi-bullseye", ID: "ioc", Name: "IOC"}}, Timeformat: "yyyy-MM-dd hh:mm:ss"}},
&updateSchema{ID: "update-user-simple-login", Name: "users", DataType: "user", Schema: `{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"roles":{"items":{"type":"string"},"type":"array"},"salt":{"type":"string"},"sha256":{"type":"string"},"sha512":{"type":"string"}},"required":["blocked","apikey","roles"],"$id":"#/definitions/User"}`},
}, nil
}
+80 -28
View File
@@ -3,8 +3,10 @@ package database
import (
"context"
"crypto/sha256"
"crypto/sha512"
"errors"
"fmt"
"log"
"math/rand"
"github.com/arangodb/go-driver"
@@ -32,13 +34,15 @@ func generateKey() string {
return string(b)
}
func toUser(user *model.UserForm, sha256 *string) *model.User {
func toUser(user *model.UserForm, salt, sha256, sha512 *string) *model.User {
roles := []string{}
roles = append(roles, role.Strings(role.Explodes(user.Roles))...)
u := &model.User{
Blocked: user.Blocked,
Roles: roles,
Salt: salt,
Sha256: sha256,
Sha512: sha512,
Apikey: user.Apikey,
}
@@ -87,21 +91,21 @@ func (db *Database) UserGetOrCreate(ctx context.Context, newUser *model.UserForm
}
func (db *Database) UserCreate(ctx context.Context, newUser *model.UserForm) (*model.NewUserResponse, error) {
var key string
var hash *string
var key, salt, sha256Hash, sha512Hash *string
if newUser.Apikey {
key = generateKey()
hash = pointer.String(fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
key, sha256Hash = generateAPIKey()
} else {
salt, sha512Hash = hashUserPassword(newUser)
}
var doc model.User
newctx := driver.WithReturnNew(ctx, &doc)
meta, err := db.userCollection.CreateDocument(ctx, newctx, strcase.ToKebab(newUser.ID), toUser(newUser, hash))
meta, err := db.userCollection.CreateDocument(ctx, newctx, strcase.ToKebab(newUser.ID), toUser(newUser, salt, sha256Hash, sha512Hash))
if err != nil {
return nil, err
}
return toNewUserResponse(meta.Key, &doc, pointer.String(key)), nil
return toNewUserResponse(meta.Key, &doc, key), nil
}
func (db *Database) UserCreateSetupAPIKey(ctx context.Context, key string) (*model.UserResponse, error) {
@@ -111,11 +115,42 @@ func (db *Database) UserCreateSetupAPIKey(ctx context.Context, key string) (*mod
Apikey: true,
Blocked: false,
}
hash := pointer.String(fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
sha256Hash := pointer.String(fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
var doc model.User
newctx := driver.WithReturnNew(ctx, &doc)
meta, err := db.userCollection.CreateDocument(ctx, newctx, strcase.ToKebab(newUser.ID), toUser(newUser, hash))
meta, err := db.userCollection.CreateDocument(ctx, newctx, strcase.ToKebab(newUser.ID), toUser(newUser, nil, sha256Hash, nil))
if err != nil {
return nil, err
}
return toUserResponse(meta.Key, &doc), nil
}
func (db *Database) UserUpdate(ctx context.Context, id string, user *model.UserForm) (*model.UserResponse, error) {
var doc model.User
_, err := db.userCollection.ReadDocument(ctx, id, &doc)
if err != nil {
return nil, err
}
if doc.Apikey {
return nil, errors.New("cannot update an API key")
}
var salt, sha512Hash *string
if user.Password != nil {
salt, sha512Hash = hashUserPassword(user)
} else {
salt = doc.Salt
sha512Hash = doc.Sha512
}
ctx = driver.WithReturnNew(ctx, &doc)
user.ID = id
meta, err := db.userCollection.ReplaceDocument(ctx, id, toUser(user, salt, nil, sha512Hash))
if err != nil {
return nil, err
}
@@ -162,12 +197,13 @@ func (db *Database) UserList(ctx context.Context) ([]*model.UserResponse, error)
return docs, err
}
func (db *Database) UserByHash(ctx context.Context, sha256 string) (*model.UserResponse, error) {
func (db *Database) UserAPIKeyByHash(ctx context.Context, sha256 string) (*model.UserResponse, error) {
query := `FOR d in @@collection
FILTER d.sha256 == @sha256
FILTER d.apikey && d.sha256 == @sha256
RETURN d`
cursor, _, err := db.Query(ctx, query, map[string]any{"@collection": UserCollectionName, "sha256": sha256}, busdb.ReadOperation)
vars := map[string]any{"@collection": UserCollectionName, "sha256": sha256}
cursor, _, err := db.Query(ctx, query, vars, busdb.ReadOperation)
if err != nil {
return nil, err
}
@@ -182,25 +218,41 @@ func (db *Database) UserByHash(ctx context.Context, sha256 string) (*model.UserR
return toUserResponse(meta.Key, &doc), err
}
func (db *Database) UserUpdate(ctx context.Context, id string, user *model.UserForm) (*model.UserResponse, error) {
func (db *Database) UserByIDAndPassword(ctx context.Context, id, password string) (*model.UserResponse, error) {
log.Println("UserByIDAndPassword", id, password)
query := `FOR d in @@collection
FILTER d._key == @id && !d.apikey && d.sha512 == SHA512(CONCAT(d.salt, @password))
RETURN d`
vars := map[string]any{"@collection": UserCollectionName, "id": id, "password": password}
cursor, _, err := db.Query(ctx, query, vars, busdb.ReadOperation)
if err != nil {
return nil, err
}
defer cursor.Close()
var doc model.User
_, err := db.userCollection.ReadDocument(ctx, id, &doc)
meta, err := cursor.ReadDocument(ctx, &doc)
if err != nil {
return nil, err
}
if doc.Sha256 != nil {
return nil, errors.New("cannot update an API key")
}
ctx = driver.WithReturnNew(ctx, &doc)
user.ID = id
meta, err := db.userCollection.ReplaceDocument(ctx, id, toUser(user, nil))
if err != nil {
return nil, err
}
return toUserResponse(meta.Key, &doc), nil
return toUserResponse(meta.Key, &doc), err
}
func generateAPIKey() (key, sha256Hash *string) {
newKey := generateKey()
sha256Hash = pointer.String(fmt.Sprintf("%x", sha256.Sum256([]byte(newKey))))
return &newKey, sha256Hash
}
func hashUserPassword(newUser *model.UserForm) (salt, sha512Hash *string) {
if newUser.Password != nil {
saltKey := generateKey()
salt = &saltKey
sha512Hash = pointer.String(fmt.Sprintf("%x", sha512.Sum512([]byte(saltKey+*newUser.Password))))
}
return salt, sha512Hash
}
+3
View File
@@ -90,6 +90,7 @@ definitions:
required: [ id, blocked, roles, apikey ]
properties:
id: { type: string }
password: { type: string }
blocked: { type: boolean }
apikey: { type: boolean }
roles: { type: array, items: { type: string } }
@@ -101,7 +102,9 @@ definitions:
blocked: { type: boolean }
apikey: { type: boolean }
roles: { type: array, items: { type: string } }
salt: { type: string }
sha256: { type: string } # for api keys
sha512: { type: string } # for users
UserResponse:
type: object
+52
View File
@@ -0,0 +1,52 @@
version: '2.4'
services:
nginx:
image: nginx:1.21
volumes:
- ./nginx-with-keycloak.conf:/etc/nginx/nginx.conf:ro
ports: [ "80:80", "8529:8529", "9000:9000", "9002:9002", "9003:9003" ]
networks: [ catalyst ]
arangodb:
image: arangodb/arangodb:3.8.1
environment:
ARANGO_ROOT_PASSWORD: foobar
networks: [ catalyst ]
minio:
image: minio/minio:RELEASE.2021-12-10T23-03-39Z
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio123
command: server /data -console-address ":9003"
networks: [ catalyst ]
postgres:
image: postgres:13
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
networks: [ catalyst ]
keycloak:
image: quay.io/keycloak/keycloak:14.0.0
environment:
DB_VENDOR: POSTGRES
DB_ADDR: postgres
DB_DATABASE: keycloak
DB_USER: keycloak
DB_SCHEMA: public
DB_PASSWORD: password
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: admin
KEYCLOAK_IMPORT: /tmp/realm.json
PROXY_ADDRESS_FORWARDING: "true"
volumes:
- ./keycloak/realm.json:/tmp/realm.json
depends_on: [ postgres ]
networks: [ catalyst ]
networks:
catalyst:
name: catalyst
+1 -27
View File
@@ -4,7 +4,7 @@ services:
image: nginx:1.21
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports: [ "80:80", "8529:8529", "9000:9000", "9001:9001", "9002:9002", "9003:9003" ]
ports: [ "80:80", "8529:8529", "9000:9000", "9003:9003" ]
networks: [ catalyst ]
arangodb:
@@ -21,32 +21,6 @@ services:
command: server /data -console-address ":9003"
networks: [ catalyst ]
postgres:
image: postgres:13
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
networks: [ catalyst ]
keycloak:
image: quay.io/keycloak/keycloak:14.0.0
environment:
DB_VENDOR: POSTGRES
DB_ADDR: postgres
DB_DATABASE: keycloak
DB_USER: keycloak
DB_SCHEMA: public
DB_PASSWORD: password
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: admin
KEYCLOAK_IMPORT: /tmp/realm.json
PROXY_ADDRESS_FORWARDING: "true"
volumes:
- ./keycloak/realm.json:/tmp/realm.json
depends_on: [ postgres ]
networks: [ catalyst ]
networks:
catalyst:
name: catalyst
+2 -2
View File
@@ -455,8 +455,8 @@
"secret": "d3ec0d91-b6ea-482d-8a4e-2f5a7ca0b4cb",
"redirectUris": [
"http://catalyst.internal.com/*",
"http://localhost:8000/callback",
"http://localhost/callback"
"http://localhost:8000/auth/callback",
"http://localhost/auth/callback"
],
"webOrigins": [
"http://catalyst.internal.com",
+112
View File
@@ -0,0 +1,112 @@
user www-data;
worker_processes 5;
error_log /var/log/nginx/error.log;
events {
worker_connections 4096;
}
http {
include mime.types;
index index.html index.htm;
log_format main '$remote_addr - $remote_user [$time_local] $status '
'"$request" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
server {
listen 80 default_server;
server_name _;
location / {
resolver 127.0.0.11 valid=30s;
set $upstream_catalyst host.docker.internal;
proxy_pass http://$upstream_catalyst:8000;
}
location /wss {
resolver 127.0.0.11 valid=30s;
set $upstream_catalyst host.docker.internal;
proxy_pass http://$upstream_catalyst:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
}
server {
listen 8529 default_server;
server_name _;
location / {
resolver 127.0.0.11 valid=30s;
set $upstream_arangodb arangodb;
proxy_pass http://$upstream_arangodb:8529;
}
}
server {
listen 9000 default_server;
server_name _;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_connect_timeout 300;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
resolver 127.0.0.11 valid=30s;
set $upstream_minio minio;
proxy_pass http://$upstream_minio:9000;
}
}
server {
listen 9002 default_server;
server_name _;
location / {
resolver 127.0.0.11 valid=30s;
set $upstream_keycloak keycloak;
proxy_pass http://$upstream_keycloak:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
}
}
server {
listen 9003 default_server;
server_name _;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_connect_timeout 300;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
resolver 127.0.0.11 valid=30s;
set $upstream_minio minio;
proxy_pass http://$upstream_minio:9003;
}
}
}
-18
View File
@@ -70,24 +70,6 @@ http {
}
}
server {
listen 9002 default_server;
server_name _;
location / {
resolver 127.0.0.11 valid=30s;
set $upstream_keycloak keycloak;
proxy_pass http://$upstream_keycloak:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
}
}
server {
listen 9003 default_server;
server_name _;
+2 -1
View File
@@ -1,5 +1,6 @@
export SECRET=4ef5b29539b70233dd40c02a1799d25079595565e05a193b09da2c3e60ada1cd
# export OIDC_ENABLE=true
export OIDC_ISSUER=http://localhost:9002/auth/realms/catalyst
export OIDC_CLIENT_SECRET=d3ec0d91-b6ea-482d-8a4e-2f5a7ca0b4cb
@@ -15,4 +16,4 @@ export EXTERNAL_ADDRESS=http://localhost
export CATALYST_ADDRESS=http://host.docker.internal
export INITIAL_API_KEY=d0169af94c40981eb4452a42fae536b6caa9be3a
go run cmd/catalyst-dev/*.go
go run ../cmd/catalyst-dev/*.go
+20
View File
@@ -0,0 +1,20 @@
export SECRET=4ef5b29539b70233dd40c02a1799d25079595565e05a193b09da2c3e60ada1cd
export SIMPLE_AUTH_ENABLE=false
export OIDC_ENABLE=true
export OIDC_ISSUER=http://localhost:9002/auth/realms/catalyst
export OIDC_CLIENT_SECRET=d3ec0d91-b6ea-482d-8a4e-2f5a7ca0b4cb
export ARANGO_DB_HOST=http://localhost:8529
export ARANGO_DB_PASSWORD=foobar
export S3_HOST=http://localhost:9000
export S3_PASSWORD=minio123
export AUTH_BLOCK_NEW=false
export AUTH_DEFAULT_ROLES=analyst,admin
export EXTERNAL_ADDRESS=http://localhost
export CATALYST_ADDRESS=http://host.docker.internal
export INITIAL_API_KEY=d0169af94c40981eb4452a42fae536b6caa9be3a
go run ../cmd/catalyst-dev/*.go
+3 -3
View File
@@ -118,7 +118,7 @@ func parseQueryOptionalBoolArray(r *http.Request, key string) ([]bool, error) {
return parseQueryBoolArray(r, key)
}
func parseBody(b []byte, i any) error {
func parseBody(b []byte, i interface{}) error {
dec := json.NewDecoder(bytes.NewBuffer(b))
err := dec.Decode(i)
if err != nil {
@@ -137,7 +137,7 @@ func JSONErrorStatus(w http.ResponseWriter, status int, err error) {
w.Write(b)
}
func response(w http.ResponseWriter, v any, err error) {
func response(w http.ResponseWriter, v interface{}, err error) {
if err != nil {
var httpError *HTTPError
if errors.As(err, &httpError) {
@@ -172,7 +172,7 @@ func validateSchema(body []byte, schema *gojsonschema.Schema, w http.ResponseWri
validationErrors = append(validationErrors, valdiationError.String())
}
b, _ := json.Marshal(map[string]any{"error": "wrong input", "errors": validationErrors})
b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": validationErrors})
w.Write(b)
return true
}
+5 -5
View File
@@ -19,7 +19,7 @@ type Service interface {
CurrentUser(context.Context) (*model.UserResponse, error)
CurrentUserData(context.Context) (*model.UserDataResponse, error)
UpdateCurrentUserData(context.Context, *model.UserData) (*model.UserDataResponse, error)
DashboardData(context.Context, string, *string) (map[string]any, error)
DashboardData(context.Context, string, *string) (map[string]interface{}, error)
ListDashboards(context.Context) ([]*model.DashboardResponse, error)
CreateDashboard(context.Context, *model.Dashboard) (*model.DashboardResponse, error)
GetDashboard(context.Context, string) (*model.DashboardResponse, error)
@@ -60,8 +60,8 @@ type Service interface {
RemoveComment(context.Context, int64, int) (*model.TicketWithTickets, error)
AddTicketPlaybook(context.Context, int64, *model.PlaybookTemplateForm) (*model.TicketWithTickets, error)
RemoveTicketPlaybook(context.Context, int64, string) (*model.TicketWithTickets, error)
SetTaskData(context.Context, int64, string, string, map[string]any) (*model.TicketWithTickets, error)
CompleteTask(context.Context, int64, string, string, map[string]any) (*model.TicketWithTickets, error)
SetTaskData(context.Context, int64, string, string, map[string]interface{}) (*model.TicketWithTickets, error)
CompleteTask(context.Context, int64, string, string, map[string]interface{}) (*model.TicketWithTickets, error)
SetTaskOwner(context.Context, int64, string, string, string) (*model.TicketWithTickets, error)
RunTask(context.Context, int64, string, string) error
SetReferences(context.Context, int64, *model.ReferenceArray) (*model.TicketWithTickets, error)
@@ -901,7 +901,7 @@ func (s *server) setTaskDataHandler(w http.ResponseWriter, r *http.Request) {
return
}
var dataP map[string]any
var dataP map[string]interface{}
if err := parseBody(body, &dataP); err != nil {
JSONError(w, err)
return
@@ -928,7 +928,7 @@ func (s *server) completeTaskHandler(w http.ResponseWriter, r *http.Request) {
return
}
var dataP map[string]any
var dataP map[string]interface{}
if err := parseBody(body, &dataP); err != nil {
JSONError(w, err)
return
+89 -89
View File
@@ -5,11 +5,11 @@ import "time"
type Args struct {
Method string
URL string
Data any
Data interface{}
}
type Want struct {
Status int
Body any
Body interface{}
}
var Tests = []struct {
@@ -23,16 +23,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/automations"},
Want: Want{
Status: 200,
Body: []any{map[string]any{"id": "comment", "image": "docker.io/python:3", "script": "", "type": []any{"playbook"}}, map[string]any{"id": "hash.sha1", "image": "docker.io/python:3", "schema": "{\"title\":\"Input\",\"type\":\"object\",\"properties\":{\"default\":{\"type\":\"string\",\"title\":\"Value\"}},\"required\":[\"default\"]}", "script": "", "type": []any{"global", "artifact", "playbook"}}, map[string]any{"id": "vt.hash", "image": "docker.io/python:3", "schema": "{\"title\":\"Input\",\"type\":\"object\",\"properties\":{\"default\":{\"type\":\"string\",\"title\":\"Value\"}},\"required\":[\"default\"]}", "script": "", "type": []any{"global", "artifact", "playbook"}}},
Body: []interface{}{map[string]interface{}{"id": "comment", "image": "docker.io/python:3", "script": "", "type": []interface{}{"playbook"}}, map[string]interface{}{"id": "hash.sha1", "image": "docker.io/python:3", "schema": "{\"title\":\"Input\",\"type\":\"object\",\"properties\":{\"default\":{\"type\":\"string\",\"title\":\"Value\"}},\"required\":[\"default\"]}", "script": "", "type": []interface{}{"global", "artifact", "playbook"}}, map[string]interface{}{"id": "vt.hash", "image": "docker.io/python:3", "schema": "{\"title\":\"Input\",\"type\":\"object\",\"properties\":{\"default\":{\"type\":\"string\",\"title\":\"Value\"}},\"required\":[\"default\"]}", "script": "", "type": []interface{}{"global", "artifact", "playbook"}}},
},
},
{
Name: "CreateAutomation",
Args: Args{Method: "Post", URL: "/automations", Data: map[string]any{"id": "hash-sha-256", "image": "docker.io/python:3", "script": "import sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha256 = hashlib.sha256(msg['payload']['default'].encode('utf-8'))\n return {'hash': sha256.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []any{"global"}}},
Args: Args{Method: "Post", URL: "/automations", Data: map[string]interface{}{"id": "hash-sha-256", "image": "docker.io/python:3", "script": "import sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha256 = hashlib.sha256(msg['payload']['default'].encode('utf-8'))\n return {'hash': sha256.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []interface{}{"global"}}},
Want: Want{
Status: 200,
Body: map[string]any{"id": "hash-sha-256", "image": "docker.io/python:3", "script": "import sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha256 = hashlib.sha256(msg['payload']['default'].encode('utf-8'))\n return {'hash': sha256.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []any{"global"}},
Body: map[string]interface{}{"id": "hash-sha-256", "image": "docker.io/python:3", "script": "import sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha256 = hashlib.sha256(msg['payload']['default'].encode('utf-8'))\n return {'hash': sha256.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []interface{}{"global"}},
},
},
@@ -41,16 +41,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/automations/hash.sha1"},
Want: Want{
Status: 200,
Body: map[string]any{"id": "hash.sha1", "image": "docker.io/python:3", "schema": "{\"title\":\"Input\",\"type\":\"object\",\"properties\":{\"default\":{\"type\":\"string\",\"title\":\"Value\"}},\"required\":[\"default\"]}", "script": "#!/usr/bin/env python\n\nimport sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha1 = hashlib.sha1(msg['payload']['default'].encode('utf-8'))\n return {\"hash\": sha1.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []any{"global", "artifact", "playbook"}},
Body: map[string]interface{}{"id": "hash.sha1", "image": "docker.io/python:3", "schema": "{\"title\":\"Input\",\"type\":\"object\",\"properties\":{\"default\":{\"type\":\"string\",\"title\":\"Value\"}},\"required\":[\"default\"]}", "script": "#!/usr/bin/env python\n\nimport sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha1 = hashlib.sha1(msg['payload']['default'].encode('utf-8'))\n return {\"hash\": sha1.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []interface{}{"global", "artifact", "playbook"}},
},
},
{
Name: "UpdateAutomation",
Args: Args{Method: "Put", URL: "/automations/hash.sha1", Data: map[string]any{"id": "hash.sha1", "image": "docker.io/python:3", "script": "import sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha1 = hashlib.sha1(msg['payload'].encode('utf-8'))\n return {'hash': sha1.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []any{"global", "artifact", "playbook"}}},
Args: Args{Method: "Put", URL: "/automations/hash.sha1", Data: map[string]interface{}{"id": "hash.sha1", "image": "docker.io/python:3", "script": "import sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha1 = hashlib.sha1(msg['payload'].encode('utf-8'))\n return {'hash': sha1.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []interface{}{"global", "artifact", "playbook"}}},
Want: Want{
Status: 200,
Body: map[string]any{"id": "hash.sha1", "image": "docker.io/python:3", "script": "import sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha1 = hashlib.sha1(msg['payload'].encode('utf-8'))\n return {'hash': sha1.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []any{"global", "artifact", "playbook"}},
Body: map[string]interface{}{"id": "hash.sha1", "image": "docker.io/python:3", "script": "import sys\nimport json\nimport hashlib\n\n\ndef run(msg):\n sha1 = hashlib.sha1(msg['payload'].encode('utf-8'))\n return {'hash': sha1.hexdigest()}\n\n\nprint(json.dumps(run(json.loads(sys.argv[1]))))\n", "type": []interface{}{"global", "artifact", "playbook"}},
},
},
@@ -68,7 +68,7 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/currentuser"},
Want: Want{
Status: 200,
Body: map[string]any{"apikey": false, "blocked": false, "id": "bob", "roles": []any{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
},
},
@@ -77,16 +77,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/currentuserdata"},
Want: Want{
Status: 200,
Body: map[string]any{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"},
Body: map[string]interface{}{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"},
},
},
{
Name: "UpdateCurrentUserData",
Args: Args{Method: "Put", URL: "/currentuserdata", Data: map[string]any{"email": "bob@example.org", "name": "Bob Bad"}},
Args: Args{Method: "Put", URL: "/currentuserdata", Data: map[string]interface{}{"email": "bob@example.org", "name": "Bob Bad"}},
Want: Want{
Status: 200,
Body: map[string]any{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"},
Body: map[string]interface{}{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"},
},
},
@@ -95,7 +95,7 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/dashboard/data?aggregation=type&filter=status+%3D%3D+%22closed%22"},
Want: Want{
Status: 200,
Body: map[string]any{"alert": 2, "incident": 1},
Body: map[string]interface{}{"alert": 2, "incident": 1},
},
},
@@ -104,16 +104,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/dashboards"},
Want: Want{
Status: 200,
Body: []any{map[string]any{"id": "simple", "name": "Simple", "widgets": []any{map[string]any{"aggregation": "owner", "filter": "status == \"open\"", "name": "open_tickets_per_user", "type": "bar", "width": 4}, map[string]any{"aggregation": "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))", "name": "tickets_per_week", "type": "line", "width": 8}}}},
Body: []interface{}{map[string]interface{}{"id": "simple", "name": "Simple", "widgets": []interface{}{map[string]interface{}{"aggregation": "owner", "filter": "status == \"open\"", "name": "open_tickets_per_user", "type": "bar", "width": 4}, map[string]interface{}{"aggregation": "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))", "name": "tickets_per_week", "type": "line", "width": 8}}}},
},
},
{
Name: "CreateDashboard",
Args: Args{Method: "Post", URL: "/dashboards", Data: map[string]any{"name": "My Dashboard", "widgets": []any{}}},
Args: Args{Method: "Post", URL: "/dashboards", Data: map[string]interface{}{"name": "My Dashboard", "widgets": []interface{}{}}},
Want: Want{
Status: 200,
Body: map[string]any{"id": "my-dashboard", "name": "My Dashboard", "widgets": []any{}},
Body: map[string]interface{}{"id": "my-dashboard", "name": "My Dashboard", "widgets": []interface{}{}},
},
},
@@ -122,16 +122,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/dashboards/simple"},
Want: Want{
Status: 200,
Body: map[string]any{"id": "simple", "name": "Simple", "widgets": []any{map[string]any{"aggregation": "owner", "filter": "status == \"open\"", "name": "open_tickets_per_user", "type": "bar", "width": 4}, map[string]any{"aggregation": "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))", "name": "tickets_per_week", "type": "line", "width": 8}}},
Body: map[string]interface{}{"id": "simple", "name": "Simple", "widgets": []interface{}{map[string]interface{}{"aggregation": "owner", "filter": "status == \"open\"", "name": "open_tickets_per_user", "type": "bar", "width": 4}, map[string]interface{}{"aggregation": "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))", "name": "tickets_per_week", "type": "line", "width": 8}}},
},
},
{
Name: "UpdateDashboard",
Args: Args{Method: "Put", URL: "/dashboards/simple", Data: map[string]any{"name": "Simple", "widgets": []any{}}},
Args: Args{Method: "Put", URL: "/dashboards/simple", Data: map[string]interface{}{"name": "Simple", "widgets": []interface{}{}}},
Want: Want{
Status: 200,
Body: map[string]any{"id": "simple", "name": "Simple", "widgets": []any{}},
Body: map[string]interface{}{"id": "simple", "name": "Simple", "widgets": []interface{}{}},
},
},
@@ -149,16 +149,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/jobs"},
Want: Want{
Status: 200,
Body: []any{map[string]any{"automation": "hash.sha1", "id": "b81c2366-ea37-43d2-b61b-03afdc21d985", "payload": "test", "status": "created"}},
Body: []interface{}{map[string]interface{}{"automation": "hash.sha1", "id": "b81c2366-ea37-43d2-b61b-03afdc21d985", "payload": "test", "status": "created"}},
},
},
{
Name: "RunJob",
Args: Args{Method: "Post", URL: "/jobs", Data: map[string]any{"automation": "hash.sha1", "payload": "test"}},
Args: Args{Method: "Post", URL: "/jobs", Data: map[string]interface{}{"automation": "hash.sha1", "payload": "test"}},
Want: Want{
Status: 200,
Body: map[string]any{"automation": "hash.sha1", "id": "87390749-2125-4a87-91c5-da7e3f9bebf1", "payload": "test", "status": "created"},
Body: map[string]interface{}{"automation": "hash.sha1", "id": "87390749-2125-4a87-91c5-da7e3f9bebf1", "payload": "test", "status": "created"},
},
},
@@ -167,16 +167,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/jobs/b81c2366-ea37-43d2-b61b-03afdc21d985"},
Want: Want{
Status: 200,
Body: map[string]any{"automation": "hash.sha1", "id": "b81c2366-ea37-43d2-b61b-03afdc21d985", "payload": "test", "status": "created"},
Body: map[string]interface{}{"automation": "hash.sha1", "id": "b81c2366-ea37-43d2-b61b-03afdc21d985", "payload": "test", "status": "created"},
},
},
{
Name: "UpdateJob",
Args: Args{Method: "Put", URL: "/jobs/b81c2366-ea37-43d2-b61b-03afdc21d985", Data: map[string]any{"running": false, "status": "failed"}},
Args: Args{Method: "Put", URL: "/jobs/b81c2366-ea37-43d2-b61b-03afdc21d985", Data: map[string]interface{}{"running": false, "status": "failed"}},
Want: Want{
Status: 200,
Body: map[string]any{"automation": "hash.sha1", "id": "b81c2366-ea37-43d2-b61b-03afdc21d985", "payload": "test", "status": "failed"},
Body: map[string]interface{}{"automation": "hash.sha1", "id": "b81c2366-ea37-43d2-b61b-03afdc21d985", "payload": "test", "status": "failed"},
},
},
@@ -185,7 +185,7 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/logs/tickets%252F294511"},
Want: Want{
Status: 200,
Body: []any{map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "creator": "bob", "message": "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.", "reference": "tickets/294511", "type": "manual"}},
Body: []interface{}{map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "creator": "bob", "message": "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.", "reference": "tickets/294511", "type": "manual"}},
},
},
@@ -194,16 +194,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/playbooks"},
Want: Want{
Status: 200,
Body: []any{map[string]any{"id": "malware", "name": "Malware", "yaml": "name: Malware\ntasks:\n file-or-hash:\n name: Do you have the file or the hash?\n type: input\n schema:\n title: Malware\n type: object\n properties:\n file:\n type: string\n title: \"I have the\"\n enum: [ \"File\", \"Hash\" ]\n next:\n enter-hash: \"file == 'Hash'\"\n upload: \"file == 'File'\"\n\n enter-hash:\n name: Please enter the hash\n type: input\n schema:\n title: Malware\n type: object\n properties:\n hash:\n type: string\n title: Please enter the hash value\n minlength: 32\n next:\n virustotal: \"hash != ''\"\n\n upload:\n name: Upload the malware\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: object\n x-display: file\n title: Please upload the malware\n next:\n hash: \"malware\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['upload'].data['malware']\"\n next:\n virustotal:\n\n virustotal:\n name: Send hash to VirusTotal\n type: automation\n automation: vt.hash\n args:\n hash: \"playbook.tasks['enter-hash'].data['hash'] || playbook.tasks['hash'].data['hash']\"\n # next:\n # known-malware: \"score > 5\"\n # sandbox: \"score < 6\" # unknown-malware\n"}, map[string]any{"id": "phishing", "name": "Phishing", "yaml": "name: Phishing\ntasks:\n board:\n name: Board Involvement?\n description: Is a board member involved?\n type: input\n schema:\n properties:\n boardInvolved:\n default: false\n title: A board member is involved.\n type: boolean\n required:\n - boardInvolved\n title: Board Involvement?\n type: object\n next:\n escalate: \"boardInvolved == true\"\n mail-available: \"boardInvolved == false\"\n\n escalate:\n name: Escalate to CISO\n description: Please escalate the task to the CISO\n type: task\n\n mail-available:\n name: Mail available\n type: input\n schema:\n oneOf:\n - properties:\n mail:\n title: Mail\n type: string\n x-display: textarea\n schemaKey:\n const: 'yes'\n type: string\n required:\n - mail\n title: 'Yes'\n - properties:\n schemaKey:\n const: 'no'\n type: string\n title: 'No'\n title: Mail available\n type: object\n next:\n block-sender: \"schemaKey == 'yes'\"\n extract-iocs: \"schemaKey == 'yes'\"\n search-email-gateway: \"schemaKey == 'no'\"\n\n search-email-gateway:\n name: Search email gateway\n description: Please search email-gateway for the phishing mail.\n type: task\n next:\n extract-iocs:\n\n block-sender:\n name: Block sender\n type: task\n next:\n extract-iocs:\n\n extract-iocs:\n name: Extract IOCs\n description: Please insert the IOCs\n type: input\n schema:\n properties:\n iocs:\n items:\n type: string\n title: IOCs\n type: array\n title: Extract IOCs\n type: object\n next:\n block-iocs:\n\n block-iocs:\n name: Block IOCs\n type: task\n"}, map[string]any{"id": "simple", "name": "Simple", "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"}},
Body: []interface{}{map[string]interface{}{"id": "malware", "name": "Malware", "yaml": "name: Malware\ntasks:\n file-or-hash:\n name: Do you have the file or the hash?\n type: input\n schema:\n title: Malware\n type: object\n properties:\n file:\n type: string\n title: \"I have the\"\n enum: [ \"File\", \"Hash\" ]\n next:\n enter-hash: \"file == 'Hash'\"\n upload: \"file == 'File'\"\n\n enter-hash:\n name: Please enter the hash\n type: input\n schema:\n title: Malware\n type: object\n properties:\n hash:\n type: string\n title: Please enter the hash value\n minlength: 32\n next:\n virustotal: \"hash != ''\"\n\n upload:\n name: Upload the malware\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: object\n x-display: file\n title: Please upload the malware\n next:\n hash: \"malware\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['upload'].data['malware']\"\n next:\n virustotal:\n\n virustotal:\n name: Send hash to VirusTotal\n type: automation\n automation: vt.hash\n args:\n hash: \"playbook.tasks['enter-hash'].data['hash'] || playbook.tasks['hash'].data['hash']\"\n # next:\n # known-malware: \"score > 5\"\n # sandbox: \"score < 6\" # unknown-malware\n"}, map[string]interface{}{"id": "phishing", "name": "Phishing", "yaml": "name: Phishing\ntasks:\n board:\n name: Board Involvement?\n description: Is a board member involved?\n type: input\n schema:\n properties:\n boardInvolved:\n default: false\n title: A board member is involved.\n type: boolean\n required:\n - boardInvolved\n title: Board Involvement?\n type: object\n next:\n escalate: \"boardInvolved == true\"\n mail-available: \"boardInvolved == false\"\n\n escalate:\n name: Escalate to CISO\n description: Please escalate the task to the CISO\n type: task\n\n mail-available:\n name: Mail available\n type: input\n schema:\n oneOf:\n - properties:\n mail:\n title: Mail\n type: string\n x-display: textarea\n schemaKey:\n const: 'yes'\n type: string\n required:\n - mail\n title: 'Yes'\n - properties:\n schemaKey:\n const: 'no'\n type: string\n title: 'No'\n title: Mail available\n type: object\n next:\n block-sender: \"schemaKey == 'yes'\"\n extract-iocs: \"schemaKey == 'yes'\"\n search-email-gateway: \"schemaKey == 'no'\"\n\n search-email-gateway:\n name: Search email gateway\n description: Please search email-gateway for the phishing mail.\n type: task\n next:\n extract-iocs:\n\n block-sender:\n name: Block sender\n type: task\n next:\n extract-iocs:\n\n extract-iocs:\n name: Extract IOCs\n description: Please insert the IOCs\n type: input\n schema:\n properties:\n iocs:\n items:\n type: string\n title: IOCs\n type: array\n title: Extract IOCs\n type: object\n next:\n block-iocs:\n\n block-iocs:\n name: Block IOCs\n type: task\n"}, map[string]interface{}{"id": "simple", "name": "Simple", "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"}},
},
},
{
Name: "CreatePlaybook",
Args: Args{Method: "Post", URL: "/playbooks", Data: map[string]any{"yaml": "name: Simple2\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"}},
Args: Args{Method: "Post", URL: "/playbooks", Data: map[string]interface{}{"yaml": "name: Simple2\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"}},
Want: Want{
Status: 200,
Body: map[string]any{"id": "simple-2", "name": "Simple2", "yaml": "name: Simple2\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"},
Body: map[string]interface{}{"id": "simple-2", "name": "Simple2", "yaml": "name: Simple2\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"},
},
},
@@ -212,16 +212,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/playbooks/simple"},
Want: Want{
Status: 200,
Body: map[string]any{"id": "simple", "name": "Simple", "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"},
Body: map[string]interface{}{"id": "simple", "name": "Simple", "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"},
},
},
{
Name: "UpdatePlaybook",
Args: Args{Method: "Put", URL: "/playbooks/simple", Data: map[string]any{"yaml": "name: Simple\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"}},
Args: Args{Method: "Put", URL: "/playbooks/simple", Data: map[string]interface{}{"yaml": "name: Simple\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"}},
Want: Want{
Status: 200,
Body: map[string]any{"id": "simple", "name": "Simple", "yaml": "name: Simple\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"},
Body: map[string]interface{}{"id": "simple", "name": "Simple", "yaml": "name: Simple\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"},
},
},
@@ -239,16 +239,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/settings"},
Want: Want{
Status: 200,
Body: map[string]any{"artifactKinds": []any{map[string]any{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]any{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []any{map[string]any{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]any{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]any{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []any{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []any{map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "yyyy-MM-dd hh:mm:ss", "version": "0.0.0-test"},
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "yyyy-MM-dd hh:mm:ss", "version": "0.0.0-test"},
},
},
{
Name: "SaveSettings",
Args: Args{Method: "Post", URL: "/settings", Data: map[string]any{"artifactKinds": []any{map[string]any{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]any{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []any{map[string]any{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]any{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]any{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "timeformat": "yyyy-MM-dd hh:mm:ss"}},
Args: Args{Method: "Post", URL: "/settings", Data: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "timeformat": "yyyy-MM-dd hh:mm:ss"}},
Want: Want{
Status: 200,
Body: map[string]any{"artifactKinds": []any{map[string]any{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]any{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []any{map[string]any{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]any{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]any{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []any{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []any{map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "yyyy-MM-dd hh:mm:ss", "version": "0.0.0-test"},
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "yyyy-MM-dd hh:mm:ss", "version": "0.0.0-test"},
},
},
@@ -257,7 +257,7 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/statistics"},
Want: Want{
Status: 200,
Body: map[string]any{"open_tickets_per_user": map[string]any{}, "tickets_per_type": map[string]any{"alert": 2, "incident": 1}, "tickets_per_week": map[string]any{"2021-39": 3}, "unassigned": 0},
Body: map[string]interface{}{"open_tickets_per_user": map[string]interface{}{}, "tickets_per_type": map[string]interface{}{"alert": 2, "incident": 1}, "tickets_per_week": map[string]interface{}{"2021-39": 3}, "unassigned": 0},
},
},
@@ -275,16 +275,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/templates"},
Want: Want{
Status: 200,
Body: []any{map[string]any{"id": "default", "name": "Default", "schema": "{\n \"definitions\": {},\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://example.com/object1618746510.json\",\n \"title\": \"Default\",\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 \"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 \"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"}},
Body: []interface{}{map[string]interface{}{"id": "default", "name": "Default", "schema": "{\n \"definitions\": {},\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://example.com/object1618746510.json\",\n \"title\": \"Default\",\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 \"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 \"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"}},
},
},
{
Name: "CreateTemplate",
Args: Args{Method: "Post", URL: "/templates", Data: map[string]any{"name": "My Template", "schema": "{\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"}},
Args: Args{Method: "Post", URL: "/templates", Data: map[string]interface{}{"name": "My Template", "schema": "{\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"}},
Want: Want{
Status: 200,
Body: map[string]any{"id": "my-template", "name": "My Template", "schema": "{\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"},
Body: map[string]interface{}{"id": "my-template", "name": "My Template", "schema": "{\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"},
},
},
@@ -293,16 +293,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/templates/default"},
Want: Want{
Status: 200,
Body: map[string]any{"id": "default", "name": "Default", "schema": "{\n \"definitions\": {},\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://example.com/object1618746510.json\",\n \"title\": \"Default\",\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 \"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 \"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"},
Body: map[string]interface{}{"id": "default", "name": "Default", "schema": "{\n \"definitions\": {},\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://example.com/object1618746510.json\",\n \"title\": \"Default\",\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 \"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 \"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"},
},
},
{
Name: "UpdateTemplate",
Args: Args{Method: "Put", URL: "/templates/default", Data: map[string]any{"name": "My Template", "schema": "{\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"}},
Args: Args{Method: "Put", URL: "/templates/default", Data: map[string]interface{}{"name": "My Template", "schema": "{\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"}},
Want: Want{
Status: 200,
Body: map[string]any{"id": "default", "name": "My Template", "schema": "{\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"},
Body: map[string]interface{}{"id": "default", "name": "My Template", "schema": "{\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"},
},
},
@@ -320,22 +320,22 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/tickets"},
Want: Want{
Status: 200,
Body: map[string]any{"count": 3, "tickets": []any{map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "type": "task"}, "block-sender": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "type": "task"}, "board": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "type": "task"}, "extract-iocs": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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\": \"GreeLine truncated
Body: map[string]interface{}{"count": 3, "tickets": []interface{}{map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "type": "task"}, "block-sender": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "type": "task"}, "board": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "type": "task"}, "extract-iocs": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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\Line truncated
},
},
{
Name: "CreateTicket",
Args: Args{Method: "Post", URL: "/tickets", Data: map[string]any{"id": 123, "name": "Wannacry infection", "owner": "bob", "status": "open", "type": "incident"}},
Args: Args{Method: "Post", URL: "/tickets", Data: map[string]interface{}{"id": 123, "name": "Wannacry infection", "owner": "bob", "status": "open", "type": "incident"}},
Want: Want{
Status: 200,
Body: map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "id": 123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "Wannacry infection", "owner": "bob", "schema": "{}", "status": "open", "type": "incident"},
Body: map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "id": 123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "Wannacry infection", "owner": "bob", "schema": "{}", "status": "open", "type": "incident"},
},
},
{
Name: "CreateTicketBatch",
Args: Args{Method: "Post", URL: "/tickets/batch", Data: []any{map[string]any{"id": 123, "name": "Wannacry infection", "owner": "bob", "status": "open", "type": "incident"}}},
Args: Args{Method: "Post", URL: "/tickets/batch", Data: []interface{}{map[string]interface{}{"id": 123, "name": "Wannacry infection", "owner": "bob", "status": "open", "type": "incident"}}},
Want: Want{
Status: 204,
Body: nil,
@@ -347,16 +347,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/tickets/8125"},
Want: Want{
Status: 200,
Body: map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "references": []any{map[string]any{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]any{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]any{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []any{map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []any{map[string]any{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]any{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]any{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
Body: map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]interface{}{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]interface{}{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []interface{}{map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]interface{}{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]interface{}{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
},
},
{
Name: "UpdateTicket",
Args: Args{Method: "Put", URL: "/tickets/8125", Data: map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.org detected", "owner": "demo", "references": []any{map[string]any{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]any{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]any{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "type": "alert"}},
Args: Args{Method: "Put", URL: "/tickets/8125", Data: map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.org detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]interface{}{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]interface{}{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "type": "alert"}},
Want: Want{
Status: 200,
Body: map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "id": 8125, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.org detected", "owner": "demo", "references": []any{map[string]any{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]any{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]any{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []any{map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []any{map[string]any{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]any{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]any{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
Body: map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "id": 8125, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.org detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]interface{}{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]interface{}{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []interface{}{map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]interface{}{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]interface{}{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
},
},
@@ -371,10 +371,10 @@ var Tests = []struct {
{
Name: "AddArtifact",
Args: Args{Method: "Post", URL: "/tickets/8123/artifacts", Data: map[string]any{"name": "2.2.2.2"}},
Args: Args{Method: "Post", URL: "/tickets/8123/artifacts", Data: map[string]interface{}{"name": "2.2.2.2"}},
Want: Want{
Status: 200,
Body: map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"name": "leadreintermediate.io", "status": "malicious"}, map[string]any{"name": "2.2.2.2", "status": "unknown", "type": "ip"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]any{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "order": 5, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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\": \"iLine truncated
Body: map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"name": "leadreintermediate.io", "status": "malicious"}, map[string]interface{}{"name": "2.2.2.2", "status": "unknown", "type": "ip"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]interface{}{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "order": 5, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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\":Line truncated
},
},
@@ -383,16 +383,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/tickets/8123/artifacts/leadreintermediate.io"},
Want: Want{
Status: 200,
Body: map[string]any{"name": "leadreintermediate.io", "status": "malicious"},
Body: map[string]interface{}{"name": "leadreintermediate.io", "status": "malicious"},
},
},
{
Name: "SetArtifact",
Args: Args{Method: "Put", URL: "/tickets/8123/artifacts/leadreintermediate.io", Data: map[string]any{"name": "leadreintermediate.io", "status": "clean"}},
Args: Args{Method: "Put", URL: "/tickets/8123/artifacts/leadreintermediate.io", Data: map[string]interface{}{"name": "leadreintermediate.io", "status": "clean"}},
Want: Want{
Status: 200,
Body: map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"name": "leadreintermediate.io", "status": "clean"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]any{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "order": 5, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 {\Line truncated
Body: map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"name": "leadreintermediate.io", "status": "clean"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]interface{}{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "order": 5, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 \Line truncated
},
},
@@ -401,16 +401,16 @@ var Tests = []struct {
Args: Args{Method: "Delete", URL: "/tickets/8123/artifacts/leadreintermediate.io"},
Want: Want{
Status: 200,
Body: map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]any{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "order": 5, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 Line truncated
Body: map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]interface{}{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "order": 5, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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\",\Line truncated
},
},
{
Name: "EnrichArtifact",
Args: Args{Method: "Post", URL: "/tickets/8123/artifacts/leadreintermediate.io/enrich", Data: map[string]any{"data": map[string]any{"hash": "b7a067a742c20d07a7456646de89bc2d408a1153"}, "name": "hash.sha1"}},
Args: Args{Method: "Post", URL: "/tickets/8123/artifacts/leadreintermediate.io/enrich", Data: map[string]interface{}{"data": map[string]interface{}{"hash": "b7a067a742c20d07a7456646de89bc2d408a1153"}, "name": "hash.sha1"}},
Want: Want{
Status: 200,
Body: map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"enrichments": map[string]any{"hash.sha1": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "data": map[string]any{"hash": "b7a067a742c20d07a7456646de89bc2d408a1153"}, "name": "hash.sha1"}}, "name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]any{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "order": 5, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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\": [\nLine truncated
Body: map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"enrichments": map[string]interface{}{"hash.sha1": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "data": map[string]interface{}{"hash": "b7a067a742c20d07a7456646de89bc2d408a1153"}, "name": "hash.sha1"}}, "name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]interface{}{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "order": 5, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 Line truncated
},
},
@@ -425,10 +425,10 @@ var Tests = []struct {
{
Name: "AddComment",
Args: Args{Method: "Post", URL: "/tickets/8125/comments", Data: map[string]any{"message": "My first comment"}},
Args: Args{Method: "Post", URL: "/tickets/8125/comments", Data: map[string]interface{}{"message": "My first comment"}},
Want: Want{
Status: 200,
Body: map[string]any{"comments": []any{map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "creator": "bob", "message": "My first comment"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "references": []any{map[string]any{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]any{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]any{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []any{map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []any{map[string]any{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]any{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]any{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
Body: map[string]interface{}{"comments": []interface{}{map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "creator": "bob", "message": "My first comment"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]interface{}{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]interface{}{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []interface{}{map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]interface{}{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]interface{}{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
},
},
@@ -437,16 +437,16 @@ var Tests = []struct {
Args: Args{Method: "Delete", URL: "/tickets/8123/comments/0"},
Want: Want{
Status: 200,
Body: map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]any{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "order": 5, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 {Line truncated
Body: map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]interface{}{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "order": 5, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 Line truncated
},
},
{
Name: "AddTicketPlaybook",
Args: Args{Method: "Post", URL: "/tickets/8125/playbooks", Data: map[string]any{"yaml": "name: Simple\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"}},
Args: Args{Method: "Post", URL: "/tickets/8125/playbooks", Data: map[string]interface{}{"yaml": "name: Simple\ntasks:\n input:\n name: Upload malware if possible\n type: input\n schema:\n title: Malware\n type: object\n properties:\n malware:\n type: string\n title: Select malware\n default: \"\"\n next:\n hash: \"malware != ''\"\n\n hash:\n name: Hash the malware\n type: automation\n automation: hash.sha1\n payload:\n default: \"playbook.tasks['input'].data['malware']\"\n next:\n escalate:\n\n escalate:\n name: Escalate to malware team\n type: task\n"}},
Want: Want{
Status: 200,
Body: map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "playbooks": map[string]any{"simple": map[string]any{"name": "Simple", "tasks": map[string]any{"escalate": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to malware team", "order": 2, "type": "task"}, "hash": map[string]any{"active": false, "automation": "hash.sha1", "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Hash the malware", "next": map[string]any{"escalate": ""}, "order": 1, "payload": map[string]any{"default": "playbook.tasks['input'].data['malware']"}, "type": "automation"}, "input": map[string]any{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Upload malware if possible", "next": map[string]any{"hash": "malware != ''"}, "order": 0, "schema": map[string]any{"properties": map[string]any{"malware": map[string]any{"default": "", "title": "Select malware", "type": "string"}}, "title": "Malware", "type": "object"}, "type": "input"}}}}, "references": []any{map[string]any{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]any{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]any{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []any{map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []any{map[string]any{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]any{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]any{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
Body: map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "playbooks": map[string]interface{}{"simple": map[string]interface{}{"name": "Simple", "tasks": map[string]interface{}{"escalate": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to malware team", "order": 2, "type": "task"}, "hash": map[string]interface{}{"active": false, "automation": "hash.sha1", "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Hash the malware", "next": map[string]interface{}{"escalate": ""}, "order": 1, "payload": map[string]interface{}{"default": "playbook.tasks['input'].data['malware']"}, "type": "automation"}, "input": map[string]interface{}{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Upload malware if possible", "next": map[string]interface{}{"hash": "malware != ''"}, "order": 0, "schema": map[string]interface{}{"properties": map[string]interface{}{"malware": map[string]interface{}{"default": "", "title": "Select malware", "type": "string"}}, "title": "Malware", "type": "object"}, "type": "input"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]interface{}{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]interface{}{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []interface{}{map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]interface{}{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]interface{}{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
},
},
@@ -455,25 +455,25 @@ var Tests = []struct {
Args: Args{Method: "Delete", URL: "/tickets/8123/playbooks/phishing"},
Want: Want{
Status: 200,
Body: map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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"},
Body: map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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"},
},
},
{
Name: "SetTaskData",
Args: Args{Method: "Put", URL: "/tickets/8123/playbooks/phishing/task/board", Data: map[string]any{"boardInvolved": true}},
Args: Args{Method: "Put", URL: "/tickets/8123/playbooks/phishing/task/board", Data: map[string]interface{}{"boardInvolved": true}},
Want: Want{
Status: 200,
Body: map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]any{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "data": map[string]any{"boardInvolved": true}, "done": false, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "order": 5, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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-itemILine truncated
Body: map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]interface{}{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "data": map[string]interface{}{"boardInvolved": true}, "done": false, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "order": 5, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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-uLine truncated
},
},
{
Name: "CompleteTask",
Args: Args{Method: "Put", URL: "/tickets/8123/playbooks/phishing/task/board/complete", Data: map[string]any{"boardInvolved": true}},
Args: Args{Method: "Put", URL: "/tickets/8123/playbooks/phishing/task/board/complete", Data: map[string]interface{}{"boardInvolved": true}},
Want: Want{
Status: 200,
Body: map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]any{"active": false, "closed": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "data": map[string]any{"boardInvolved": true}, "done": true, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "order": 5, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 Line truncated
Body: map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]interface{}{"active": false, "closed": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "data": map[string]interface{}{"boardInvolved": true}, "done": true, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "order": 5, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 Line truncated
},
},
@@ -482,7 +482,7 @@ var Tests = []struct {
Args: Args{Method: "Put", URL: "/tickets/8123/playbooks/phishing/task/board/owner", Data: "eve"},
Want: Want{
Status: 200,
Body: map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]any{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "owner": "eve", "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "order": 5, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 \"oneOfLine truncated
Body: map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "order": 6, "type": "task"}, "block-sender": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "order": 3, "type": "task"}, "board": map[string]interface{}{"active": true, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "order": 0, "owner": "eve", "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "order": 1, "type": "task"}, "extract-iocs": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "order": 5, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "order": 2, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"active": false, "created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "order": 4, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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 \"Line truncated
},
},
@@ -497,10 +497,10 @@ var Tests = []struct {
{
Name: "SetReferences",
Args: Args{Method: "Put", URL: "/tickets/8125/references", Data: []any{map[string]any{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}},
Args: Args{Method: "Put", URL: "/tickets/8125/references", Data: []interface{}{map[string]interface{}{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}},
Want: Want{
Status: 200,
Body: map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "references": []any{map[string]any{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []any{map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []any{map[string]any{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]any{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]any{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
Body: map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []interface{}{map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]interface{}{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]interface{}{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
},
},
@@ -509,7 +509,7 @@ var Tests = []struct {
Args: Args{Method: "Put", URL: "/tickets/8125/schema", Data: "{}"},
Want: Want{
Status: 200,
Body: map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "references": []any{map[string]any{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]any{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]any{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []any{map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []any{map[string]any{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]any{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]any{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
Body: map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8125, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "phishing from selenafadel@von.com detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "https://www.seniorleading-edge.name/users/efficient", "name": "recovery"}, map[string]interface{}{"href": "http://www.dynamicseamless.com/clicks-and-mortar", "name": "force"}, map[string]interface{}{"href": "http://www.leadscalable.biz/envisioneer", "name": "fund"}}, "schema": "{}", "status": "closed", "tickets": []interface{}{map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]interface{}{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]interface{}{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"}}, "type": "alert"},
},
},
@@ -518,7 +518,7 @@ var Tests = []struct {
Args: Args{Method: "Patch", URL: "/tickets/8126/tickets", Data: 8123},
Want: Want{
Status: 200,
Body: map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []any{map[string]any{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]any{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]any{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "tickets": []any{map[string]any{"artifacts": []any{map[string]any{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]any{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]any{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]any{"phishing": map[string]any{"name": "Phishing", "tasks": map[string]any{"block-iocs": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "type": "task"}, "block-sender": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]any{"extract-iocs": ""}, "type": "task"}, "board": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]any{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "schema": map[string]any{"properties": map[string]any{"boardInvolved": map[string]any{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []any{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "type": "task"}, "extract-iocs": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]any{"block-iocs": ""}, "schema": map[string]any{"properties": map[string]any{"iocs": map[string]any{"items": map[string]any{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]any{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "schema": map[string]any{"oneOf": []any{map[string]any{"properties": map[string]any{"mail": map[string]any{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]any{"const": "yes", "type": "string"}}, "required": []any{"mail"}, "title": "Yes"}, map[string]any{"properties": map[string]any{"schemaKey": map[string]any{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]any{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]any{"extract-iocs": ""}, "type": "task"}}}}, "references": []any{map[string]any{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]any{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]any{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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\": \"Line truncated
Body: map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]interface{}{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]interface{}{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "tickets": []interface{}{map[string]interface{}{"artifacts": []interface{}{map[string]interface{}{"name": "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", "status": "unknown"}, map[string]interface{}{"name": "http://www.customerviral.io/scalable/vertical/killer", "status": "clean"}, map[string]interface{}{"name": "leadreintermediate.io", "status": "malicious"}}, "created": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "id": 8123, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78206000, time.UTC), "name": "live zebra", "owner": "demo", "playbooks": map[string]interface{}{"phishing": map[string]interface{}{"name": "Phishing", "tasks": map[string]interface{}{"block-iocs": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block IOCs", "type": "task"}, "block-sender": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Block sender", "next": map[string]interface{}{"extract-iocs": ""}, "type": "task"}, "board": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Board Involvement?", "next": map[string]interface{}{"escalate": "boardInvolved == true", "mail-available": "boardInvolved == false"}, "schema": map[string]interface{}{"properties": map[string]interface{}{"boardInvolved": map[string]interface{}{"default": false, "title": "A board member is involved.", "type": "boolean"}}, "required": []interface{}{"boardInvolved"}, "title": "Board Involvement?", "type": "object"}, "type": "input"}, "escalate": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Escalate to CISO", "type": "task"}, "extract-iocs": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Extract IOCs", "next": map[string]interface{}{"block-iocs": ""}, "schema": map[string]interface{}{"properties": map[string]interface{}{"iocs": map[string]interface{}{"items": map[string]interface{}{"type": "string"}, "title": "IOCs", "type": "array"}}, "title": "Extract IOCs", "type": "object"}, "type": "input"}, "mail-available": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Mail available", "next": map[string]interface{}{"block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'"}, "schema": map[string]interface{}{"oneOf": []interface{}{map[string]interface{}{"properties": map[string]interface{}{"mail": map[string]interface{}{"title": "Mail", "type": "string", "x-display": "textarea"}, "schemaKey": map[string]interface{}{"const": "yes", "type": "string"}}, "required": []interface{}{"mail"}, "title": "Yes"}, map[string]interface{}{"properties": map[string]interface{}{"schemaKey": map[string]interface{}{"const": "no", "type": "string"}}, "title": "No"}}, "title": "Mail available", "type": "object"}, "type": "input"}, "search-email-gateway": map[string]interface{}{"created": time.Date(2021, time.December, 12, 12, 12, 12, 12, time.UTC), "done": false, "name": "Search email gateway", "next": map[string]interface{}{"extract-iocs": ""}, "type": "task"}}}}, "references": []interface{}{map[string]interface{}{"href": "https://www.leadmaximize.net/e-services/back-end", "name": "performance"}, map[string]interface{}{"href": "http://www.corporateinteractive.name/rich", "name": "autumn"}, map[string]interface{}{"href": "https://www.corporateintuitive.org/intuitive/platforms/integrate", "name": "suggest"}}, "schema": "{\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\Line truncated
},
},
@@ -527,7 +527,7 @@ var Tests = []struct {
Args: Args{Method: "Delete", URL: "/tickets/8126/tickets", Data: 8125},
Want: Want{
Status: 200,
Body: map[string]any{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []any{map[string]any{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]any{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]any{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"},
Body: map[string]interface{}{"created": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "id": 8126, "modified": time.Date(2021, time.October, 2, 16, 4, 59, 78186000, time.UTC), "name": "Surfaceintroduce virus detected", "owner": "demo", "references": []interface{}{map[string]interface{}{"href": "http://www.centralworld-class.io/synthesize", "name": "university"}, map[string]interface{}{"href": "https://www.futurevirtual.org/supply-chains/markets/sticky/iterate", "name": "goal"}, map[string]interface{}{"href": "http://www.chiefsyndicate.io/action-items", "name": "unemployment"}}, "schema": "{}", "status": "closed", "type": "alert"},
},
},
@@ -536,16 +536,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/tickettypes"},
Want: Want{
Status: 200,
Body: []any{map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}},
Body: []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}},
},
},
{
Name: "CreateTicketType",
Args: Args{Method: "Post", URL: "/tickettypes", Data: map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-newspaper-variant-outline", "name": "TI Tickets"}},
Args: Args{Method: "Post", URL: "/tickettypes", Data: map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-newspaper-variant-outline", "name": "TI Tickets"}},
Want: Want{
Status: 200,
Body: map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-newspaper-variant-outline", "id": "ti-tickets", "name": "TI Tickets"},
Body: map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-newspaper-variant-outline", "id": "ti-tickets", "name": "TI Tickets"},
},
},
@@ -554,16 +554,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/tickettypes/alert"},
Want: Want{
Status: 200,
Body: map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"},
Body: map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"},
},
},
{
Name: "UpdateTicketType",
Args: Args{Method: "Put", URL: "/tickettypes/alert", Data: map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-bell", "id": "alert", "name": "Alerts"}},
Args: Args{Method: "Put", URL: "/tickettypes/alert", Data: map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-bell", "id": "alert", "name": "Alerts"}},
Want: Want{
Status: 200,
Body: map[string]any{"default_playbooks": []any{}, "default_template": "default", "icon": "mdi-bell", "id": "alert", "name": "Alerts"},
Body: map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-bell", "id": "alert", "name": "Alerts"},
},
},
@@ -581,7 +581,7 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/userdata"},
Want: Want{
Status: 200,
Body: []any{map[string]any{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"}},
Body: []interface{}{map[string]interface{}{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"}},
},
},
@@ -590,16 +590,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/userdata/bob"},
Want: Want{
Status: 200,
Body: map[string]any{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"},
Body: map[string]interface{}{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"},
},
},
{
Name: "UpdateUserData",
Args: Args{Method: "Put", URL: "/userdata/bob", Data: map[string]any{"blocked": false, "email": "bob@example.org", "name": "Bob Bad"}},
Args: Args{Method: "Put", URL: "/userdata/bob", Data: map[string]interface{}{"blocked": false, "email": "bob@example.org", "name": "Bob Bad"}},
Want: Want{
Status: 200,
Body: map[string]any{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"},
Body: map[string]interface{}{"email": "bob@example.org", "id": "bob", "name": "Bob Bad"},
},
},
@@ -608,16 +608,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/users"},
Want: Want{
Status: 200,
Body: []any{map[string]any{"apikey": false, "blocked": false, "id": "bob", "roles": []any{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}, map[string]any{"apikey": true, "blocked": false, "id": "script", "roles": []any{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}},
Body: []interface{}{map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}, map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}},
},
},
{
Name: "CreateUser",
Args: Args{Method: "Post", URL: "/users", Data: map[string]any{"apikey": true, "blocked": false, "id": "syncscript", "roles": []any{"analyst"}}},
Args: Args{Method: "Post", URL: "/users", Data: map[string]interface{}{"apikey": true, "blocked": false, "id": "syncscript", "roles": []interface{}{"analyst"}}},
Want: Want{
Status: 200,
Body: map[string]any{"blocked": false, "id": "syncscript", "roles": []any{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read"}, "secret": "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"},
Body: map[string]interface{}{"blocked": false, "id": "syncscript", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read"}, "secret": "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"},
},
},
@@ -626,16 +626,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/users/script"},
Want: Want{
Status: 200,
Body: map[string]any{"apikey": true, "blocked": false, "id": "script", "roles": []any{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
Body: map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
},
},
{
Name: "UpdateUser",
Args: Args{Method: "Put", URL: "/users/bob", Data: map[string]any{"apikey": false, "blocked": false, "id": "syncscript", "roles": []any{"analyst", "admin"}}},
Args: Args{Method: "Put", URL: "/users/bob", Data: map[string]interface{}{"apikey": false, "blocked": false, "id": "syncscript", "roles": []interface{}{"analyst", "admin"}}},
Want: Want{
Status: 200,
Body: map[string]any{"apikey": false, "blocked": false, "id": "bob", "roles": []any{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
},
},
+9
View File
@@ -7089,8 +7089,14 @@
},
"type" : "array"
},
"salt" : {
"type" : "string"
},
"sha256" : {
"type" : "string"
},
"sha512" : {
"type" : "string"
}
},
"required" : [ "apikey", "blocked", "roles" ],
@@ -7149,6 +7155,9 @@
"id" : {
"type" : "string"
},
"password" : {
"type" : "string"
},
"roles" : {
"items" : {
"type" : "string"
+6
View File
@@ -1285,8 +1285,12 @@ definitions:
items:
type: string
type: array
salt:
type: string
sha256:
type: string
sha512:
type: string
required:
- blocked
- apikey
@@ -1334,6 +1338,8 @@ definitions:
type: boolean
id:
type: string
password:
type: string
roles:
items:
type: string
+9
View File
@@ -6510,8 +6510,14 @@
},
"type" : "array"
},
"salt" : {
"type" : "string"
},
"sha256" : {
"type" : "string"
},
"sha512" : {
"type" : "string"
}
},
"required" : [ "apikey", "blocked", "roles" ],
@@ -6570,6 +6576,9 @@
"id" : {
"type" : "string"
},
"password" : {
"type" : "string"
},
"roles" : {
"items" : {
"type" : "string"
+6
View File
@@ -1166,8 +1166,12 @@ definitions:
items:
type: string
type: array
salt:
type: string
sha256:
type: string
sha512:
type: string
required:
- blocked
- apikey
@@ -1215,6 +1219,8 @@ definitions:
type: boolean
id:
type: string
password:
type: string
roles:
items:
type: string
+99 -96
View File
@@ -116,10 +116,10 @@ func init() {
gojsonschema.NewStringLoader(`{"type":"object","properties":{"default_groups":{"items":{"type":"string"},"type":"array"},"default_playbooks":{"items":{"type":"string"},"type":"array"},"default_template":{"type":"string"},"icon":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name","icon","default_template","default_playbooks"],"$id":"#/definitions/TicketTypeResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"logs":{"items":{"$ref":"#/definitions/LogEntry"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/PlaybookResponse"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"tickets":{"items":{"$ref":"#/definitions/TicketSimpleResponse"},"type":"array"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"required":["id","name","type","status","created","modified","schema"],"$id":"#/definitions/TicketWithTickets"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"color":{"title":"Color","type":"string","enum":["error","info","success","warning"]},"icon":{"title":"Icon (https://materialdesignicons.com)","type":"string"},"id":{"title":"ID","type":"string"},"name":{"title":"Name","type":"string"}},"required":["id","name","icon"],"$id":"#/definitions/Type"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"roles":{"items":{"type":"string"},"type":"array"},"sha256":{"type":"string"}},"required":["blocked","apikey","roles"],"$id":"#/definitions/User"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"roles":{"items":{"type":"string"},"type":"array"},"salt":{"type":"string"},"sha256":{"type":"string"},"sha512":{"type":"string"}},"required":["blocked","apikey","roles"],"$id":"#/definitions/User"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"email":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"$id":"#/definitions/UserData"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"email":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"required":["id"],"$id":"#/definitions/UserDataResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"password":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"aggregation":{"type":"string"},"filter":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["bar","line","pie"]},"width":{"maximum":12,"type":"integer"}},"required":["name","type","aggregation","width"],"$id":"#/definitions/Widget"}`),
)
@@ -251,14 +251,14 @@ type DashboardResponse struct {
}
type Enrichment struct {
Created time.Time `json:"created"`
Data map[string]any `json:"data"`
Name string `json:"name"`
Created time.Time `json:"created"`
Data map[string]interface{} `json:"data"`
Name string `json:"name"`
}
type EnrichmentForm struct {
Data map[string]any `json:"data"`
Name string `json:"name"`
Data map[string]interface{} `json:"data"`
Name string `json:"name"`
}
type File struct {
@@ -267,39 +267,39 @@ type File struct {
}
type Job struct {
Automation string `json:"automation"`
Container *string `json:"container,omitempty"`
Log *string `json:"log,omitempty"`
Origin *Origin `json:"origin,omitempty"`
Output map[string]any `json:"output,omitempty"`
Payload any `json:"payload,omitempty"`
Running bool `json:"running"`
Status string `json:"status"`
Automation string `json:"automation"`
Container *string `json:"container,omitempty"`
Log *string `json:"log,omitempty"`
Origin *Origin `json:"origin,omitempty"`
Output map[string]interface{} `json:"output,omitempty"`
Payload interface{} `json:"payload,omitempty"`
Running bool `json:"running"`
Status string `json:"status"`
}
type JobForm struct {
Automation string `json:"automation"`
Origin *Origin `json:"origin,omitempty"`
Payload any `json:"payload,omitempty"`
Automation string `json:"automation"`
Origin *Origin `json:"origin,omitempty"`
Payload interface{} `json:"payload,omitempty"`
}
type JobResponse struct {
Automation string `json:"automation"`
Container *string `json:"container,omitempty"`
ID string `json:"id"`
Log *string `json:"log,omitempty"`
Origin *Origin `json:"origin,omitempty"`
Output map[string]any `json:"output,omitempty"`
Payload any `json:"payload,omitempty"`
Status string `json:"status"`
Automation string `json:"automation"`
Container *string `json:"container,omitempty"`
ID string `json:"id"`
Log *string `json:"log,omitempty"`
Origin *Origin `json:"origin,omitempty"`
Output map[string]interface{} `json:"output,omitempty"`
Payload interface{} `json:"payload,omitempty"`
Status string `json:"status"`
}
type JobUpdate struct {
Container *string `json:"container,omitempty"`
Log *string `json:"log,omitempty"`
Output map[string]any `json:"output,omitempty"`
Running bool `json:"running"`
Status string `json:"status"`
Container *string `json:"container,omitempty"`
Log *string `json:"log,omitempty"`
Output map[string]interface{} `json:"output,omitempty"`
Running bool `json:"running"`
Status string `json:"status"`
}
type LogEntry struct {
@@ -312,7 +312,7 @@ type LogEntry struct {
type Message struct {
Context *Context `json:"context,omitempty"`
Payload any `json:"payload,omitempty"`
Payload interface{} `json:"payload,omitempty"`
Secrets map[string]string `json:"secrets,omitempty"`
}
@@ -385,18 +385,18 @@ type Statistics struct {
}
type Task struct {
Automation *string `json:"automation,omitempty"`
Closed *time.Time `json:"closed,omitempty"`
Created time.Time `json:"created"`
Data map[string]any `json:"data,omitempty"`
Done bool `json:"done"`
Join *bool `json:"join,omitempty"`
Name string `json:"name"`
Next map[string]string `json:"next,omitempty"`
Owner *string `json:"owner,omitempty"`
Payload map[string]string `json:"payload,omitempty"`
Schema map[string]any `json:"schema,omitempty"`
Type string `json:"type"`
Automation *string `json:"automation,omitempty"`
Closed *time.Time `json:"closed,omitempty"`
Created time.Time `json:"created"`
Data map[string]interface{} `json:"data,omitempty"`
Done bool `json:"done"`
Join *bool `json:"join,omitempty"`
Name string `json:"name"`
Next map[string]string `json:"next,omitempty"`
Owner *string `json:"owner,omitempty"`
Payload map[string]string `json:"payload,omitempty"`
Schema map[string]interface{} `json:"schema,omitempty"`
Type string `json:"type"`
}
type TaskOrigin struct {
@@ -406,20 +406,20 @@ type TaskOrigin struct {
}
type TaskResponse struct {
Active bool `json:"active"`
Automation *string `json:"automation,omitempty"`
Closed *time.Time `json:"closed,omitempty"`
Created time.Time `json:"created"`
Data map[string]any `json:"data,omitempty"`
Done bool `json:"done"`
Join *bool `json:"join,omitempty"`
Name string `json:"name"`
Next map[string]string `json:"next,omitempty"`
Order int64 `json:"order"`
Owner *string `json:"owner,omitempty"`
Payload map[string]string `json:"payload,omitempty"`
Schema map[string]any `json:"schema,omitempty"`
Type string `json:"type"`
Active bool `json:"active"`
Automation *string `json:"automation,omitempty"`
Closed *time.Time `json:"closed,omitempty"`
Created time.Time `json:"created"`
Data map[string]interface{} `json:"data,omitempty"`
Done bool `json:"done"`
Join *bool `json:"join,omitempty"`
Name string `json:"name"`
Next map[string]string `json:"next,omitempty"`
Order int64 `json:"order"`
Owner *string `json:"owner,omitempty"`
Payload map[string]string `json:"payload,omitempty"`
Schema map[string]interface{} `json:"schema,omitempty"`
Type string `json:"type"`
}
type TaskWithContext struct {
@@ -432,28 +432,28 @@ type TaskWithContext struct {
}
type Ticket struct {
Artifacts []*Artifact `json:"artifacts,omitempty"`
Comments []*Comment `json:"comments,omitempty"`
Created time.Time `json:"created"`
Details map[string]any `json:"details,omitempty"`
Files []*File `json:"files,omitempty"`
Modified time.Time `json:"modified"`
Name string `json:"name"`
Owner *string `json:"owner,omitempty"`
Playbooks map[string]*Playbook `json:"playbooks,omitempty"`
Read []string `json:"read,omitempty"`
References []*Reference `json:"references,omitempty"`
Schema string `json:"schema"`
Status string `json:"status"`
Type string `json:"type"`
Write []string `json:"write,omitempty"`
Artifacts []*Artifact `json:"artifacts,omitempty"`
Comments []*Comment `json:"comments,omitempty"`
Created time.Time `json:"created"`
Details map[string]interface{} `json:"details,omitempty"`
Files []*File `json:"files,omitempty"`
Modified time.Time `json:"modified"`
Name string `json:"name"`
Owner *string `json:"owner,omitempty"`
Playbooks map[string]*Playbook `json:"playbooks,omitempty"`
Read []string `json:"read,omitempty"`
References []*Reference `json:"references,omitempty"`
Schema string `json:"schema"`
Status string `json:"status"`
Type string `json:"type"`
Write []string `json:"write,omitempty"`
}
type TicketForm struct {
Artifacts []*Artifact `json:"artifacts,omitempty"`
Comments []*Comment `json:"comments,omitempty"`
Created *time.Time `json:"created,omitempty"`
Details map[string]any `json:"details,omitempty"`
Details map[string]interface{} `json:"details,omitempty"`
Files []*File `json:"files,omitempty"`
ID *int64 `json:"id,omitempty"`
Modified *time.Time `json:"modified,omitempty"`
@@ -479,7 +479,7 @@ type TicketResponse struct {
Artifacts []*Artifact `json:"artifacts,omitempty"`
Comments []*Comment `json:"comments,omitempty"`
Created time.Time `json:"created"`
Details map[string]any `json:"details,omitempty"`
Details map[string]interface{} `json:"details,omitempty"`
Files []*File `json:"files,omitempty"`
ID int64 `json:"id"`
Modified time.Time `json:"modified"`
@@ -495,22 +495,22 @@ type TicketResponse struct {
}
type TicketSimpleResponse struct {
Artifacts []*Artifact `json:"artifacts,omitempty"`
Comments []*Comment `json:"comments,omitempty"`
Created time.Time `json:"created"`
Details map[string]any `json:"details,omitempty"`
Files []*File `json:"files,omitempty"`
ID int64 `json:"id"`
Modified time.Time `json:"modified"`
Name string `json:"name"`
Owner *string `json:"owner,omitempty"`
Playbooks map[string]*Playbook `json:"playbooks,omitempty"`
Read []string `json:"read,omitempty"`
References []*Reference `json:"references,omitempty"`
Schema string `json:"schema"`
Status string `json:"status"`
Type string `json:"type"`
Write []string `json:"write,omitempty"`
Artifacts []*Artifact `json:"artifacts,omitempty"`
Comments []*Comment `json:"comments,omitempty"`
Created time.Time `json:"created"`
Details map[string]interface{} `json:"details,omitempty"`
Files []*File `json:"files,omitempty"`
ID int64 `json:"id"`
Modified time.Time `json:"modified"`
Name string `json:"name"`
Owner *string `json:"owner,omitempty"`
Playbooks map[string]*Playbook `json:"playbooks,omitempty"`
Read []string `json:"read,omitempty"`
References []*Reference `json:"references,omitempty"`
Schema string `json:"schema"`
Status string `json:"status"`
Type string `json:"type"`
Write []string `json:"write,omitempty"`
}
type TicketTemplate struct {
@@ -560,7 +560,7 @@ type TicketWithTickets struct {
Artifacts []*Artifact `json:"artifacts,omitempty"`
Comments []*Comment `json:"comments,omitempty"`
Created time.Time `json:"created"`
Details map[string]any `json:"details,omitempty"`
Details map[string]interface{} `json:"details,omitempty"`
Files []*File `json:"files,omitempty"`
ID int64 `json:"id"`
Logs []*LogEntry `json:"logs,omitempty"`
@@ -588,7 +588,9 @@ type User struct {
Apikey bool `json:"apikey"`
Blocked bool `json:"blocked"`
Roles []string `json:"roles"`
Salt *string `json:"salt,omitempty"`
Sha256 *string `json:"sha256,omitempty"`
Sha512 *string `json:"sha512,omitempty"`
}
type UserData struct {
@@ -607,10 +609,11 @@ type UserDataResponse struct {
}
type UserForm struct {
Apikey bool `json:"apikey"`
Blocked bool `json:"blocked"`
ID string `json:"id"`
Roles []string `json:"roles"`
Apikey bool `json:"apikey"`
Blocked bool `json:"blocked"`
ID string `json:"id"`
Password *string `json:"password,omitempty"`
Roles []string `json:"roles"`
}
type UserResponse struct {
+2 -1
View File
@@ -2,6 +2,7 @@ github.com/SecurityBrewery/catalyst/cmd/catalyst (network)
github.com/RoaringBitmap/roaring (reflect, unsafe)
github.com/SecurityBrewery/catalyst (execute, file, network)
github.com/SecurityBrewery/catalyst/auth (network)
github.com/SecurityBrewery/catalyst/database/busdb (network)
github.com/SecurityBrewery/catalyst/generated/api (network)
github.com/SecurityBrewery/catalyst/generated/caql/parser (reflect)
@@ -77,7 +78,6 @@ github.com/docker/go-connections/sockets (file, network, syscall)
github.com/docker/go-connections/tlsconfig (file)
github.com/go-chi/chi (network)
github.com/go-chi/chi/middleware (file, network)
github.com/go-chi/cors (file, network)
github.com/gobwas/ws (network, reflect, unsafe)
github.com/gobwas/ws/wsutil (file, network)
github.com/gogo/protobuf/proto (reflect, unsafe)
@@ -99,6 +99,7 @@ golang.org/x/net/internal/socks (network)
golang.org/x/net/proxy (file, network)
golang.org/x/oauth2 (network)
golang.org/x/oauth2/internal (file, network)
golang.org/x/sys/cpu (file)
golang.org/x/sys/internal/unsafeheader (unsafe)
golang.org/x/sys/unix (syscall, unsafe)
google.golang.org/genproto/googleapis/rpc/status (reflect)
+2 -2
View File
@@ -12,9 +12,9 @@ require (
github.com/coreos/go-oidc/v3 v3.2.0
github.com/docker/docker v17.12.0-ce-rc1.0.20201201034508-7d75c1d40d88+incompatible
github.com/go-chi/chi v1.5.4
github.com/go-chi/cors v1.2.1
github.com/gobwas/ws v1.1.0
github.com/google/uuid v1.3.0
github.com/gorilla/securecookie v1.1.1
github.com/iancoleman/strcase v0.2.0
github.com/icza/dyno v0.0.0-20210726202311-f1bafe5d9996
github.com/imdario/mergo v0.3.13
@@ -24,6 +24,7 @@ require (
github.com/tidwall/sjson v1.2.4
github.com/tus/tusd v1.9.0
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf
golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401
gopkg.in/yaml.v3 v3.0.1
@@ -76,7 +77,6 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/net v0.0.0-20220325170049-de3da57026de // indirect
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect
google.golang.org/appengine v1.6.7 // indirect
+2 -2
View File
@@ -372,8 +372,6 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -508,6 +506,8 @@ github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+23 -21
View File
@@ -2,14 +2,13 @@ package catalyst
import (
"context"
"io/fs"
"net/http"
"time"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
"github.com/SecurityBrewery/catalyst/auth"
"github.com/SecurityBrewery/catalyst/bus"
"github.com/SecurityBrewery/catalyst/busservice"
"github.com/SecurityBrewery/catalyst/database"
@@ -21,7 +20,6 @@ import (
"github.com/SecurityBrewery/catalyst/role"
"github.com/SecurityBrewery/catalyst/service"
"github.com/SecurityBrewery/catalyst/storage"
"github.com/SecurityBrewery/catalyst/ui"
)
type Config struct {
@@ -30,11 +28,12 @@ type Config struct {
Storage *storage.Config
Secret []byte
Auth *AuthConfig
Auth *auth.Config
ExternalAddress string
InternalAddress string
InitialAPIKey string
Network string
Port int
}
type Server struct {
@@ -47,12 +46,13 @@ type Server struct {
func New(hooks *hooks.Hooks, config *Config) (*Server, error) {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
ctx, cancel := context.WithTimeout(ctx, time.Minute*10)
defer cancel()
err := config.Auth.Load(ctx)
if err != nil {
return nil, err
if config.Auth.OIDCAuthEnable {
if err := config.Auth.Load(ctx); err != nil {
return nil, err
}
}
catalystStorage, err := storage.New(config.Storage)
@@ -109,14 +109,16 @@ func New(hooks *hooks.Hooks, config *Config) (*Server, error) {
}
func setupAPI(catalystService *service.Service, catalystStorage *storage.Storage, catalystDatabase *database.Database, dbConfig *database.Config, bus *bus.Bus, config *Config) (chi.Router, error) {
middlewares := []func(next http.Handler) http.Handler{Authenticate(catalystDatabase, config.Auth), AuthorizeBlockedUser()}
secureJar := auth.NewJar(config.Secret)
middlewares := []func(next http.Handler) http.Handler{
auth.Authenticate(catalystDatabase, config.Auth, secureJar),
auth.AuthorizeBlockedUser(),
}
// create server
apiServerMiddleware := []func(next http.Handler) http.Handler{cors.AllowAll().Handler}
apiServerMiddleware = append(apiServerMiddleware, middlewares...)
apiServer := api.NewServer(catalystService, AuthorizeRole, apiServerMiddleware...)
fileReadWrite := AuthorizeRole([]string{role.FileReadWrite.String()})
apiServer := api.NewServer(catalystService, auth.AuthorizeRole, middlewares...)
fileReadWrite := auth.AuthorizeRole([]string{role.FileReadWrite.String()})
tudHandler := tusdUpload(catalystDatabase, bus, catalystStorage.S3(), config.ExternalAddress)
apiServer.With(fileReadWrite).Head("/files/{ticketID}/tusd/{id}", tudHandler)
apiServer.With(fileReadWrite).Patch("/files/{ticketID}/tusd/{id}", tudHandler)
@@ -124,18 +126,18 @@ func setupAPI(catalystService *service.Service, catalystStorage *storage.Storage
apiServer.With(fileReadWrite).Post("/files/{ticketID}/upload", upload(catalystDatabase, catalystStorage.S3(), catalystStorage.Uploader()))
apiServer.With(fileReadWrite).Get("/files/{ticketID}/download/{key}", download(catalystStorage.Downloader()))
apiServer.With(AuthorizeRole([]string{role.BackupRead.String()})).Get("/backup/create", backupHandler(catalystStorage, dbConfig))
apiServer.With(AuthorizeRole([]string{role.BackupRestore.String()})).Post("/backup/restore", restoreHandler(catalystStorage, catalystDatabase, dbConfig))
apiServer.With(auth.AuthorizeRole([]string{role.BackupRead.String()})).Get("/backup/create", backupHandler(catalystStorage, dbConfig))
apiServer.With(auth.AuthorizeRole([]string{role.BackupRestore.String()})).Post("/backup/restore", restoreHandler(catalystStorage, catalystDatabase, dbConfig))
server := chi.NewRouter()
server.Use(middleware.RequestID, middleware.RealIP, middleware.Logger, middleware.Recoverer, cors.AllowAll().Handler)
server.Use(middleware.RequestID, middleware.RealIP, middleware.Logger, middleware.Recoverer)
server.Mount("/api", apiServer)
server.Get("/callback", callback(config.Auth))
server.With(middlewares...).Handle("/wss", handleWebSocket(bus))
server.Mount("/auth", auth.Server(config.Auth, catalystDatabase, secureJar))
fsys, _ := fs.Sub(ui.UI, "dist")
server.With(middlewares...).NotFound(api.VueStatic(fsys))
server.Get("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/ui/", http.StatusFound)
})
return server, nil
}
+1 -1
View File
@@ -33,7 +33,7 @@ func (s *Service) GetUserData(ctx context.Context, id string) (*model.UserDataRe
}
func (s *Service) UpdateUserData(ctx context.Context, id string, data *model.UserData) (doc *model.UserDataResponse, err error) {
defer s.publishRequest(ctx, err, "CreateUser", userDataResponseID(doc))
defer s.publishRequest(ctx, err, "UpdateUserData", userDataResponseID(doc))
return s.database.UserDataUpdate(ctx, id, data)
}
+9 -5
View File
@@ -15,6 +15,7 @@ import (
"golang.org/x/oauth2"
"github.com/SecurityBrewery/catalyst"
"github.com/SecurityBrewery/catalyst/auth"
"github.com/SecurityBrewery/catalyst/bus"
"github.com/SecurityBrewery/catalyst/database"
"github.com/SecurityBrewery/catalyst/database/busdb"
@@ -46,8 +47,11 @@ func Config(ctx context.Context) (*catalyst.Config, error) {
Password: "minio123",
},
Secret: []byte("4ef5b29539b70233dd40c02a1799d25079595565e05a193b09da2c3e60ada1cd"),
Auth: &catalyst.AuthConfig{
OIDCIssuer: "http://localhost:9002/auth/realms/catalyst",
Auth: &auth.Config{
SimpleAuthEnable: true,
APIKeyAuthEnable: true,
OIDCAuthEnable: true,
OIDCIssuer: "http://localhost:9002/auth/realms/catalyst",
OAuth2: &oauth2.Config{
ClientID: "catalyst",
ClientSecret: "13d4a081-7395-4f71-a911-bc098d8d3c45",
@@ -61,12 +65,12 @@ func Config(ctx context.Context) (*catalyst.Config, error) {
// AuthDefaultRoles: nil,
},
}
err := config.Auth.Load(ctx)
if err != nil {
if err := config.Auth.Load(ctx); err != nil {
return nil, err
}
return config, err
return config, nil
}
func Index(t *testing.T) (*index.Index, func(), error) {
+18 -18
View File
@@ -2,31 +2,31 @@ describe('user', () => {
it('open ticket', () => {
cy.visit('/');
// login
cy.get("#username").type("bob");
cy.get("#password").type("bob");
cy.get("#kc-login").click();
if (Cypress.env('AUTH') === 'simple') {
cy.login();
} else if (Cypress.env('AUTH') === 'keycloak') {
cy.get("#username").type("bob");
cy.get("#password").type("bob");
cy.get("#kc-login").click();
}
cy.getCookie('user').should('exist');
cy.intercept('GET', '/api/userdata/demo', { fixture: 'userdata_demo.json' })
cy.intercept('GET', '/api/users/demo', { fixture: 'user_demo.json' })
cy.origin('http://localhost', () => {
cy.visit('/tickets');
cy.visit('http://localhost/ui/tickets');
// clear caql
cy.get("#app > div > main > div > div > div > div > header > div > div.v-input.v-input--hide-details.v-input--is-label-active.v-input--is-dirty.v-input--dense.theme--light.v-text-field.v-text-field--single-line.v-text-field--solo.v-text-field--solo-flat.v-text-field--is-booted.v-text-field--enclosed.v-text-field--placeholder > div > div > div:nth-child(2) > div > button")
.click();
// clear caql
cy.get("#caqlbar > div > div > div > div > div:nth-child(2) > div > button").click();
// open ticket
cy.get("#app > div > main > div > div > div > div > div > div.v-data-table__wrapper > table > tbody > tr:nth-child(1) > td > a")
.click()
// open ticket
cy.contains("live zebra").click()
// assert url
cy.url().should('eq', "http://localhost/tickets/8123")
// assert url
cy.url().should('eq', "http://localhost/ui/tickets/8123")
// assert title
cy.get("#\\38 123 > div > div > div:nth-child(3) > div:nth-child(2) > div:nth-child(2) > div > div.col-lg-8.col-12 > h1")
.should("have.text", " Incident #8123: live zebra ")
})
// assert title
cy.get("h1").should("have.text", " Incident #8123: live zebra ")
})
})
+22
View File
@@ -0,0 +1,22 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
+17
View File
@@ -4,3 +4,20 @@
// ***********************************************************
import './commands'
Cypress.Cookies.defaults({
preserve: 'user',
})
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
Cypress.Commands.add('login', (options = {}) => {
// login
cy.contains("Name").click({force: true});
cy.get("#username").type("tom");
cy.contains("Password").click({force: true});
cy.get("#password").type("tom");
cy.get("button").contains("Login").click();
})
+237 -129
View File
@@ -1,157 +1,212 @@
<template>
<v-app class="background">
<v-navigation-drawer dark permanent :mini-variant="mini" :expand-on-hover="mini" app color="statusbar">
<v-list>
<v-list-item class="px-2" :to="{ name: 'Home' }">
<v-list-item-avatar rounded="0">
<v-img src="/static/flask_white.svg" :width="40"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="title">
Catalyst
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
<!--v-list dense nav>
<v-list-item class="px-0" dense :to="{ name: 'Profile' }">
<v-list-item-avatar>
<v-img v-if="$store.state.userdata.image" :src="$store.state.userdata.image"></v-img>
<v-icon v-else>mdi-account-circle</v-icon>
</v-list-item-avatar>
<div v-if="$store.state.user">
{{ $store.state.userdata.name }}
</div>
</v-list-item>
</v-list>
<v-divider></v-divider-->
<v-list nav dense>
<v-list-item>
<v-list-item-icon>
<v-icon class="my-1">mdi-arrow-right-bold</v-icon>
</v-list-item-icon>
<v-list-item-title>
<v-text-field
placeholder="Goto"
outlined
dense
hide-details
v-on:keyup.enter="enter"
clearable
color="#fff"
v-model="goto"></v-text-field>
</v-list-item-title>
</v-list-item>
</v-list>
<v-divider></v-divider>
<AppLink :links="internal"></AppLink>
<v-list nav dense v-if="$store.state.settings.ticketTypes">
<v-list-item
v-for="customType in $store.state.settings.ticketTypes"
:key="customType.id"
link
:class="{ 'v-list-item--active': ($route.params.type === customType.id) }"
@click="openTicketList(customType.id)">
<v-list-item-icon>
<v-badge
v-if="customType.id in counts && counts[customType.id] > 0"
:content="counts[customType.id]"
color="red"
left
offset-x="35"
offset-y="8"
bottom>
<v-icon>{{ customType.icon }}</v-icon>
</v-badge>
<v-icon v-else>{{ customType.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ customType.name }}</v-list-item-title>
</v-list-item>
</v-list>
<v-divider></v-divider>
<AppLink :links="settings"></AppLink>
<template v-slot:append>
<v-list nav dense>
<v-list-item class="version" dense style="min-height: 20px">
<div>
<v-app v-if="!authenticated" id="app" class="background-dark">
<v-container class="login d-flex flex-column justify-center">
<v-form ref="form" v-model="valid" @submit.prevent="login">
<v-card class="pa-4">
<div class="d-flex justify-center">
<v-img src="/ui/flask.svg" height="100" width="100" class="flex-grow-0"></v-img>
</div>
<v-card-title class="text-center justify-center">
Catalyst Login
</v-card-title>
<v-card-text v-if="hassimple" class="text-center">
<v-text-field id="username" name="username" label="Name" v-model="username" :rules="[
v => !!v || 'Name is required',
]"></v-text-field>
<v-text-field
id="password"
name="password"
label="Password"
:append-icon="show ? 'mdi-eye' : 'mdi-eye-off'"
:type="show ? 'text' : 'password'"
@click:append="show = !show"
v-model="password"
:rules="[
v => !!v || 'Password is required',
// v => (v && v.length > 8) || 'Password must be more than 8 characters',
]"></v-text-field>
</v-card-text>
<v-card-actions class="justify-center">
<v-btn v-if="hasoidc" text href="/auth/oidclogin">
Login with OIDC
</v-btn>
<v-spacer v-if="hassimple"></v-spacer>
<v-btn v-if="hassimple" type="submit" color="primary" elevation="0" :disabled="!valid">
Login
</v-btn>
</v-card-actions>
</v-card>
</v-form>
</v-container>
</v-app>
<v-app v-else class="background">
<v-navigation-drawer dark permanent :mini-variant="mini" :expand-on-hover="mini" app color="statusbar">
<v-list>
<v-list-item class="px-2" :to="{ name: 'Home' }">
<v-list-item-avatar rounded="0">
<v-img src="/ui/flask_white.svg" :width="40"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title style="text-align: center; opacity: 0.5;">
{{ $store.state.settings.tier }} v{{ $store.state.settings.version }}
<v-list-item-title class="title">
Catalyst
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
<v-divider></v-divider>
<v-list nav dense>
<v-list-item :to="{ name: 'API' }">
<v-list-item>
<v-list-item-icon>
<v-icon>mdi-share-variant</v-icon>
<v-icon class="my-1">mdi-arrow-right-bold</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>API Documentation</v-list-item-title>
</v-list-item-content>
<v-list-item-title>
<v-text-field
placeholder="Goto"
outlined
dense
hide-details
v-on:keyup.enter="enter"
clearable
color="#fff"
v-model="goto"></v-text-field>
</v-list-item-title>
</v-list-item>
</v-list>
</template>
</v-navigation-drawer>
<v-divider></v-divider>
<v-app-bar app dense flat absolute color="transparent">
<v-btn icon @click="mini = !mini">
<v-icon color="primary">mdi-menu</v-icon>
</v-btn>
<AppLink :links="internal"></AppLink>
<v-breadcrumbs :items="crumbs">
<template v-slot:item="{ item }">
<v-breadcrumbs-item
:to="item.to"
class="text-subtitle-2 crumb-item"
:disabled="item.disabled"
exact
>
{{ item.text }}
</v-breadcrumbs-item>
<v-list nav dense v-if="$store.state.settings.ticketTypes">
<v-list-item
v-for="customType in $store.state.settings.ticketTypes"
:key="customType.id"
link
:class="{ 'v-list-item--active': ($route.params.type === customType.id) }"
@click="openTicketList(customType.id)">
<v-list-item-icon>
<v-badge
v-if="customType.id in counts && counts[customType.id] > 0"
:content="counts[customType.id]"
color="red"
left
offset-x="35"
offset-y="8"
bottom>
<v-icon>{{ customType.icon }}</v-icon>
</v-badge>
<v-icon v-else>{{ customType.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ customType.name }}</v-list-item-title>
</v-list-item>
</v-list>
<v-divider></v-divider>
<AppLink :links="settings"></AppLink>
<template v-slot:append>
<v-list nav dense>
<v-list-item class="version" dense style="min-height: 20px">
<v-list-item-content>
<v-list-item-title style="text-align: center; opacity: 0.5;">
{{ $store.state.settings.tier }} v{{ $store.state.settings.version }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
<v-divider></v-divider>
<v-list nav dense>
<v-list-item :to="{ name: 'API' }">
<v-list-item-icon>
<v-icon>mdi-share-variant</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>API Documentation</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</template>
</v-breadcrumbs>
</v-navigation-drawer>
<v-app-bar app dense flat absolute color="transparent">
<v-btn icon @click="mini = !mini">
<v-icon id="toggle_menu" color="primary">mdi-menu</v-icon>
</v-btn>
<v-spacer></v-spacer>
<v-breadcrumbs :items="crumbs">
<template v-slot:item="{ item }">
<v-breadcrumbs-item
:to="item.to"
class="text-subtitle-2 crumb-item"
:disabled="item.disabled"
exact
>
{{ item.text }}
</v-breadcrumbs-item>
</template>
</v-breadcrumbs>
<v-btn :to="{ name: 'Profile' }" icon>
<v-avatar v-if="$store.state.userdata.image" size="32">
<v-img :src="$store.state.userdata.image"></v-img>
</v-avatar>
<v-icon v-else>mdi-account-circle</v-icon>
</v-btn>
<v-spacer></v-spacer>
</v-app-bar>
<router-view></router-view>
<v-snackbar v-model="snackbar" :color="$store.state.alert.type" :timeout="$store.state.alert.type === 'error' ? -1 : 5000" outlined>
<b style="display: block">{{ $store.state.alert.name | capitalize }}</b>
{{ $store.state.alert.detail }}
<template v-slot:action="{ attrs }">
<v-btn text v-bind="attrs" @click="snackbar = false">Close</v-btn>
</template>
</v-snackbar>
</v-app>
<v-menu left bottom offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn
icon
v-bind="attrs"
v-on="on"
>
<v-avatar v-if="$store.state.userdata.image" size="32">
<v-img :src="$store.state.userdata.image"></v-img>
</v-avatar>
<v-icon v-else>mdi-account-circle</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item :to="{ name: 'Profile' }">
<v-list-item-title>Account</v-list-item-title>
<v-list-item-icon><v-icon>mdi-account-circle</v-icon></v-list-item-icon>
</v-list-item>
<v-list-item @click="logout">
<v-list-item-title>Logout</v-list-item-title>
<v-list-item-icon><v-icon>mdi-logout</v-icon></v-list-item-icon>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<router-view></router-view>
<v-snackbar v-model="snackbar" :color="$store.state.alert.type" :timeout="$store.state.alert.type === 'error' ? -1 : 5000" outlined>
<b style="display: block">{{ $store.state.alert.name | capitalize }}</b>
{{ $store.state.alert.detail }}
<template v-slot:action="{ attrs }">
<v-btn text v-bind="attrs" @click="snackbar = false">Close</v-btn>
</template>
</v-snackbar>
</v-app>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import AppLink from "./components/AppLink.vue";
import router from "vue-router";
import {API} from "@/services/api";
export default Vue.extend({
name: "App",
components: {AppLink},
data: () => ({
show: false,
hassimple: false,
hasoidc: false,
username: "",
password: "",
valid: true,
authenticated: false,
settings: [
{ icon: "mdi-format-list-bulleted-type", name: "Ticket Types", to: "TicketTypeList", role: "engineer:tickettype:write" },
{ icon: "mdi-file-hidden", name: "Templates", to: "TemplateList", role: "analyst:template:read" },
@@ -229,12 +284,56 @@ export default Vue.extend({
return this.lodash.includes(this.$store.state.user.roles, s);
}
return false;
},
login: function () {
this.axios.post(
"/auth/login",
{username: this.username, password: this.password},
).then((response) => {
console.log(response.data);
if (!this.lodash.isObject(response.data)) {
return
}
this.$store.dispatch("getUser");
this.$store.dispatch("getUserData");
this.$store.dispatch("getSettings");
this.authenticated = true;
}).catch(() => {
this.valid = false;
})
},
logout: function () {
this.axios.post("/auth/logout").then(() => {
this.authenticated = false;
})
}
},
mounted() {
this.$store.dispatch("getUser");
this.$store.dispatch("getUserData");
this.$store.dispatch("getSettings");
this.axios.get("/auth/config").then((response) => {
this.hassimple = response.data.simple;
this.hasoidc = response.data.oidc;
API.currentUser().then((response) => {
if (!this.lodash.isObject(response.data)) {
if (!this.hassimple && this.hasoidc) {
window.location.href = "/auth/oidclogin";
}
return
}
this.authenticated = true;
this.$store.dispatch("getUser");
this.$store.dispatch("getUserData");
this.$store.dispatch("getSettings");
})
}).catch(() => {
this.hassimple = false;
this.hasoidc = false;
})
},
});
</script>
@@ -244,6 +343,15 @@ export default Vue.extend({
background-color: #f5f5f5 !important;
}
.background-dark {
background-color: #212121 !important;
}
.login {
height: 100%;
max-width: 400px !important;
}
.v-app-bar.v-toolbar--dense .v-toolbar__content {
border-bottom: 1px solid #e0e0e0 !important;
}
+18
View File
@@ -2186,12 +2186,24 @@ export interface User {
* @memberof User
*/
'roles': Array<string>;
/**
*
* @type {string}
* @memberof User
*/
'salt'?: string;
/**
*
* @type {string}
* @memberof User
*/
'sha256'?: string;
/**
*
* @type {string}
* @memberof User
*/
'sha512'?: string;
}
/**
*
@@ -2285,6 +2297,12 @@ export interface UserForm {
* @memberof UserForm
*/
'id': string;
/**
*
* @type {string}
* @memberof UserForm
*/
'password'?: string;
/**
*
* @type {Array<string>}
+1
View File
@@ -24,6 +24,7 @@
</v-row>
<v-toolbar
id="caqlbar"
rounded
filled
dense
+1
View File
@@ -275,6 +275,7 @@ const routes: Array<RouteConfig> = [
const router = new VueRouter({
mode: 'history',
base: 'ui',
routes,
});
+38 -7
View File
@@ -12,17 +12,43 @@
</div>
<div v-else class="fill-height d-flex flex-column pa-8">
<div v-if="$route.params.id === 'new'">
<h2>Create new API key</h2>
<h2>
Create a new
<span v-if="user.apikey">API Key</span>
<span v-else>User</span>
</h2>
<v-form>
<v-text-field label="ID" v-model="user.id" hide-details></v-text-field>
<v-btn-toggle v-model="user.apikey" mandatory dense>
<v-btn :value="false">User</v-btn>
<v-btn :value="true">API Key</v-btn>
</v-btn-toggle>
<v-text-field label="ID" v-model="user.id" class="mb-2" :rules="[
v => !!v || 'ID is required',
v => (v && v.length < 254) || 'ID must be between 1 and 254 characters',
v => /^[a-z\d\-]+$/.test(v) || 'Only characters a-z, 0-9 and - are allowed',
// v => /^[A-Za-z0-9_\-\:@\(\)\+,=;\$!\*'%]+$/.test(v) || 'Only characters A-Z, a-z, 0-9, _, -, :, ., @, (, ), +, ,, =, ;, $, !, *, \', % are allowed',
]"></v-text-field>
<v-text-field
v-if="!user.apikey"
label="Password"
v-model="user.password"
:append-icon="show ? 'mdi-eye' : 'mdi-eye-off'"
:type="show ? 'text' : 'password'"
@click:append="show = !show"
:rules="[
v => !!v || 'Password is required',
v => (v && v.length >= 8) || 'Password must be at least 8 characters',
]"></v-text-field>
<v-select multiple chips label="Roles" v-model="user.roles" :items="$store.state.settings.roles"></v-select>
<v-btn @click="save" color="success" outlined>
<v-icon>mdi-plus-thick</v-icon>
Create API-Key
Create
<span v-if="user.apikey" class="ml-1">API Key</span>
<span v-else class="ml-1">User</span>
</v-btn>
</v-form>
<v-alert v-if="newUserResponse" color="warning" class="mt-4" dismissible>
<b>New API-Secret:</b> {{ newUserResponse.secret }}<br>
<b>New API secret:</b> {{ newUserResponse.secret }}<br>
Make sure you save it - you won't be able to access it again.
</v-alert>
</div>
@@ -32,9 +58,12 @@
<span v-if="user.apikey">(API Key)</span>
</h2>
<v-text-field v-if="!user.apikey" label="New Password (leave empty to keep)" v-model="user.password" hide-details class="mb-4"></v-text-field>
<v-checkbox v-if="!user.apikey" label="Blocked" v-model="user.blocked" hide-details class="mb-4"></v-checkbox>
<v-select multiple chips v-if="!user.apikey" label="Roles" v-model="user.roles" :items="$store.state.settings.roles"></v-select>
<div v-else>
<v-chip v-for="role in user.roles" :key="role">{{ role }}</v-chip>
<v-chip v-for="role in user.roles" :key="role" class="mr-1 mb-1">{{ role }}</v-chip>
</div>
<v-btn v-if="!user.apikey" @click="save" color="success" outlined>
@@ -49,11 +78,12 @@
<script lang="ts">
import Vue from "vue";
import { NewUserResponse, UserResponse } from "@/client";
import {NewUserResponse, UserForm} from "@/client";
import {API} from "@/services/api";
interface State {
user?: UserResponse;
show: boolean;
user?: UserForm;
newUserResponse?: NewUserResponse;
}
@@ -61,6 +91,7 @@ export default Vue.extend({
name: "User",
components: {},
data: (): State => ({
show: false,
user: undefined,
newUserResponse: undefined
}),
+1 -1
View File
@@ -1,5 +1,5 @@
module.exports = {
publicPath: "/static/",
publicPath: "/ui/",
transpileDependencies: ["vuetify", "@koumoul/vjsf"],
pwa: {
name: "Catalyst",